lynis-report-converter/lynis-report-converter.pl
borekon f01b6f7525 Changed authoring
Created by SOLTEC
2023-04-20 09:18:04 +00:00

2569 lines
133 KiB
Perl
Executable File

#!/usr/bin/perl
use strict;
# turn off warnings so as not to confuse people
# unless debugging, etc.
#use warnings;
use feature qw( switch );
require 5.010;
no if $] ge '5.018', warnings => "experimental::smartmatch";
use Term::ANSIColor;
use Getopt::Long qw( :config no_ignore_case bundling );
use Data::Dumper;
use Module::Load::Conditional qw( can_load check_install requires );
my $VERSION = '0.3-beta';
my ($help,$input,$verbose,$excel,$output,$pdf,$debug,$json,$quiet,$xml,$showversion);
GetOptions(
'h|help' => \$help,
'i|input=s' => \$input,
'v|verbose+' => \$verbose,
'E|excel' => \$excel,
'o|output=s' => \$output,
'p|pdf' => \$pdf,
'D|debug' => \$debug,
'j|json' => \$json,
'x|xml' => \$xml,
'q|quiet' => \$quiet,
'V|version' => \$showversion,
);
&usage if ($help);
&usage if ((!$output) and (!$json) and (!$showversion));
#if ($verbose) { use warnings; }
my %to_bool = ( 0 => 'false', 1 => 'true', "" => 'false' );
my %vm_mode = ( 0 => 'false', 1 => 'guest', 2 => 'host' );
my %to_long_severity = ( 'C' => 'Critical', 'S' => 'Severe', 'H' => 'High', 'M' => 'Medium', 'L' => 'Low', 'I' => 'Informational', '-' => 'NA', "" => 'NA' );
my %systemd_uf_status_color = (
'enabled' => '#00ff00',
'disabled' => '#ff0000',
'static' => 'inherit',
'masked' => 'goldenrod'
);
my $lynis_report;
if ($json) { $quiet = 1; }
my ($basename, $path, $suffix, $htmldoc, $format);
if ($excel) {
$output = 'report.xlsx' unless ((defined($output)) and ($output ne ""));
$format = 'excel';
} elsif ($pdf) {
$output = 'report.pdf' unless ((defined($output)) and ($output ne ''));
$htmldoc = "$$.html";
$format = 'pdf';
} elsif ($json) {
$output = undef unless ((defined($output)) and ($output ne ''));
$format = 'json';
} elsif ($xml) {
$output = 'report.xml' unless ((defined($output)) and ($output ne ''));
$format = 'xml';
} else {
$output = "report.html" unless ((defined($output)) and ($output ne ""));
$htmldoc = $output;
$format = 'html';
}
if (defined($input)) {
$lynis_report = $input;
} else {
$lynis_report = '/var/log/lynis-report.dat';
}
my $lynis_log = '/var/log/lynis.log';
my $audit_run = 0; #assume false
my %lynis_report_data;
if (( -e $lynis_log) and ( ! -z $lynis_log )) {
print colored("Found lynis output log. \n", "cyan") if ($verbose);
$audit_run++;
}
if (( -e $lynis_report) and ( ! -z $lynis_report )) {
print colored("Found lynis report. \n", "cyan") if ($verbose);
$audit_run++;
}
if (($audit_run) and ($audit_run >= 1)) {
print colored("Looks like the audit has been run.", "green") unless ($quiet);
print "\n" unless ($quiet);
} else {
warn colored("Couldn't find one or more of the lynis output files. Try running the audit again. \n", "bold red");
}
unless ($quiet) {
print colored("Outputting report to $output, in ", "green");
if ($excel) { print colored("Excel ", "green"); }
elsif ($pdf) { print colored("PDF ", "green"); }
elsif ($xml) { print colored("XML ", "green"); }
elsif ($json) { print colored("JSON ", "green"); }
else { print colored("HTML ", "green"); }
print colored("format.", "green");
print "\n";
}
# Handle inconsistent keys
&pop_inconsistent_keys($format, \%lynis_report_data);
# the report is easy to process, and actually doesn't contain the "audit findings"....just the data.
# but it is not our job to draw conclusions here, just present the findings of the tool.
open RPT, "<$lynis_report" or die colored("There was a problem opening the lynis report: $! ", "bold red");
while (my $line = <RPT>) {
next if ($line =~ /^#/); # skip commented lines
#next if ($line =~ /Result.*allow\_url\_fopen.*/); # This looks like a bug in the report output. Skip it.
#next if ($line =~ /Result.*expose\_php.*/); # This looks like a bug in the report output. Skip it.
chomp($line);
#if ($line =~ /swap_partition/) { print colored("$line\n", "bold magenta"); }
my ($k,$v);
if (scalar(split(/=/, $line)) > 2) { # We got more than 2 elements after the split,
# so there is likely a equals in either the key
# or the value
if ($line =~ /^(.+?)\=(.+)/) {
$k = $1; $v = $2;
} else {
die colored("Unexpected match condition in splitting key/value pairs!", "bold red");
}
} else {
($k, $v) = split(/=/, $line);
}
if ((!defined($k)) or ($k eq "")) { next; } # something went wonky -- we didn't get a valid key. so skip
if ((!defined($v)) or ($v eq "")) {
given($format) {
when (/(excel|json)/) { $v = "NA"; }
default { $v = "&nbsp;"; } # fill with a blank(ish) value if nothing
}
}
print "k=$k\n" if (($verbose) and ($verbose > 1));
print "v=$v\n" if (($verbose) and ($verbose > 1));
# if the key already exists, assume it's supposed to be an array value. Array values are handled a couple
# different ways in the lynis report. This is just one.
if (exists($lynis_report_data{$k})) {
if (ref($lynis_report_data{$k}) eq 'ARRAY') {
push @{$lynis_report_data{$k}}, $v;
} else {
my $tmp_v = $lynis_report_data{$k};
undef($lynis_report_data{$k});
if ($tmp_v =~ /(?:\&nbsp\;|NA)/) {
push @{$lynis_report_data{$k}}, $v;
} else {
push @{$lynis_report_data{$k}}, $tmp_v, $v;
}
}
} else {
$lynis_report_data{$k} = $v;
}
}
close RPT or die colored("There was a problem closing the lynis report: $! ", "bold red");
foreach my $k ( qw(container notebook apparmor_enabled apparmor_policy_loaded ) ) {
if ($lynis_report_data{$k} != 1) { $lynis_report_data{$k} = 0; }
}
if (ref($lynis_report_data{'automation_tool_running[]'}) eq 'ARRAY') {
@{$lynis_report_data{'automation_tool_running[]'}} = &dedup_array($lynis_report_data{'automation_tool_running[]'}) if (ref($lynis_report_data{'automation_tool_running[]'}) eq 'ARRAY');
}
if (ref($lynis_report_data{'boot_service[]'}) eq 'ARRAY') {
@{$lynis_report_data{'boot_service[]'}} = &dedup_array($lynis_report_data{'boot_service[]'}) if (ref($lynis_report_data{'boot_service[]'}) eq "ARRAY");
}
if (ref($lynis_report_data{'cronjob[]'}) eq 'ARRAY') {
@{$lynis_report_data{'cronjob[]'}} = &dedup_array($lynis_report_data{'cronjob[]'}) if (ref($lynis_report_data{'cronjob[]'}) eq 'ARRAY');
}
if (ref($lynis_report_data{'nginx_config[]'}) eq 'ARRAY') {
@{$lynis_report_data{'nginx_config[]'}} = &dedup_array($lynis_report_data{'nginx_config[]'}) if (ref($lynis_report_data{'nginx_config[]'}) eq 'ARRAY');
}
if (exists($lynis_report_data{'pam_auth_brute_force_protection_module[]'})) {
if (ref($lynis_report_data{'pam_auth_brute_force_protection_module[]'}) eq 'ARRAY') {
@{$lynis_report_data{'pam_auth_brute_force_protection_module[]'}} = &dedup_array($lynis_report_data{'pam_auth_brute_force_protection_module[]'});
}
}
foreach my $key ( qw( certificates domainname journal_disk_size pop3_daemon imap_daemon printing_daemon ntp_daemon ntp_version apache_version systemd_version systemd_status systemd_builtin_components journal_coredumps_lastday running_service_tool service_manager localhost-mapped-to ) ) {
# if element is not an array we don't need to flatten it
if (ref($lynis_report_data{$key}) ne 'ARRAY') {
warn colored("Skipped flatten $key since it's not an array.", "yellow") if ($verbose);
next;
}
$lynis_report_data{$key} = &flatten(@{$lynis_report_data{$key}});
}
my $pass_score = &calc_password_complexity_score;
my (%warnings, %suggestions);
# process "string array" values delimited by a pipe (|)
foreach my $key ( sort keys %lynis_report_data ) {
print "$key, ".ref($lynis_report_data{$key})." \n" if (($verbose) and ($verbose > 1));
if (((ref($lynis_report_data{$key}) ne 'ARRAY') and
(ref($lynis_report_data{$key}) ne 'HASH')) and
($lynis_report_data{$key} =~ /\|/)) {
print colored($key."\n", "green") if (($verbose) and ($verbose > 1));
my @fs = split(/\|/, $lynis_report_data{$key});
undef($lynis_report_data{$key});
push @{$lynis_report_data{$key}}, @fs;
}
}
my (@tests_skipped, @tests_executed);
my ($lynis_version);
if (exists($lynis_report_data{'tests_skipped'})) {
@tests_skipped = @{$lynis_report_data{'tests_skipped'}};
delete($lynis_report_data{'tests_skipped'});
}
if (exists($lynis_report_data{'tests_executed'})) {
@tests_executed = @{$lynis_report_data{'tests_executed'}};
delete($lynis_report_data{'tests_executed'});
}
if ($showversion) {
&show_version;
}
if ($debug) {
print colored("In debug mode. Dumping data hash.\n", "yellow");
print color('yellow');
print Dumper(\%lynis_report_data);
print color('reset');
exit 1;
}
if ($json) {
require JSON;
# tidy up some of the "object" variables
my @sduf;
if (ref($lynis_report_data{'systemd_unit_file[]'}) eq 'ARRAY') {
@sduf = @{$lynis_report_data{'systemd_unit_file[]'}};
my @sduf_new;
foreach my $uf ( @sduf ) {
my ($name,$status) = split(/\|/, $uf);
push @sduf_new, { 'name' => $name, 'state' => $status };
}
$lynis_report_data{'systemd_unit_file[]'} = \@sduf_new;
}
my @ipa;
if (ref($lynis_report_data{'installed_packages_array'}) eq 'ARRAY') {
@ipa = @{$lynis_report_data{'installed_packages_array'}};
my @ipa_new;
foreach my $pkg ( @ipa ) {
my ($name,$vers) = split(/\,/, $pkg);
push @ipa_new, { 'name' => $name, 'version' => $vers };
}
$lynis_report_data{'installed_packages_array'} = \@ipa_new;
}
my @nlp;
if (ref($lynis_report_data{'network_listen_port[]'}) eq 'ARRAY') {
@nlp = @{$lynis_report_data{'network_listen_port[]'}};
my @nlp_new;
foreach my $pt (@nlp) {
my ($port,$proto,$proc) = split(/\|/, $pt);
push @nlp_new, { 'port' => $port, 'protocol' => $proto, 'owner_process' => $proc };
}
$lynis_report_data{'network_listen_port[]'} = \@nlp_new;
}
my @details;
if (ref($lynis_report_data{'details[]'}) eq 'ARRAY') {
@details = @{$lynis_report_data{'details[]'}};
my @det_new;
foreach my $d ( @details ) {
my ($id,$svc,$desc,$nmn) = split(/\|/, $d);
my %descr;
my @p = split(/\;/, $desc);
foreach my $p ( @p ) {
my ($k, $v) = split(/\s*\:\s*/, $p);
$descr{$k} = $v;
}
push @det_new, { 'id' => $id, 'service' => $svc, 'description' => \%descr };
}
$lynis_report_data{'details[]'} = \@det_new;
}
my @plugs;
if (ref($lynis_report_data{'plugin_enabled_phase1[]'}) eq 'ARRAY') {
@plugs = @{$lynis_report_data{'plugin_enabled_phase1[]'}} unless (!exists($lynis_report_data{'plugin_enabled_phase1[]'}));
my @plugs_new;
foreach my $p ( @plugs ) {
my ($name,$vers) = split(/\|/, $p);
push @plugs_new, { 'name' => $name, 'version' => $vers };
}
$lynis_report_data{'plugin_enabled_phase1[]'} = \@plugs_new;
}
my @suggs;
if (ref($lynis_report_data{'suggestion[]'}) eq 'ARRAY') {
@suggs = @{$lynis_report_data{'suggestion[]'}} unless (!exists($lynis_report_data{'suggestion[]'}));
my @suggs_new;
foreach my $s ( @suggs ) {
my ($id,$desc,$sev,$f4) = split(/\|/, $s);
push @suggs_new, { 'id' => $id, 'description' => $desc, 'severity' => $to_long_severity{$sev} }
}
$lynis_report_data{'suggestion[]'} = \@suggs_new;
}
my $json_obj = JSON->new->allow_nonref;
my $json_text = $json_obj->encode( \%lynis_report_data );
if ($output) {
# open the file and write to it
open OUT, ">$output" or die colored("There was a problem with the output file: $!", "bold red");
print OUT $json_text."\n";
close OUT
} else {
# it's more likely JSON consumers would want to pipe the output to another process
# so print to STDOUT
print $json_text;
}
# JSON is parsed directly from the report data array, using the JSON module. So there should be no unhandled key-value pairs.
# So just undef the hash.
undef(%lynis_report_data);
} elsif ($xml) {
require XML::Writer;
my ($xmlout,$writer);
if (($xml) and ($output)) {
require IO::File;
$xmlout = IO::File->new(">$output");
$writer = XML::Writer->new('CONTENT'=>'self','DATA_MODE'=>1,'DATA_INDENT'=>2,'OUTPUT'=>$xmlout);
} else {
$writer = XML::Writer->new('CONTENT'=>'self','DATA_MODE'=>1,'DATA_INDENT'=>2,);
}
$writer->xmlDecl('UTF-8');
$writer->startTag('lynisReportData');
foreach my $key ( sort keys %lynis_report_data ) {
if (ref($lynis_report_data{$key}) eq 'ARRAY') {
my $tmpkey = $key;
$tmpkey =~ s/\[\]//g;
given ($key) {
when (/home_directory\[\]/) {
$writer->startTag("home_directories");
foreach my $ele ( sort @{$lynis_report_data{$key}} ) {
$writer->dataElement($tmpkey, $ele);
}
$writer->endTag();
}
when (/network_listen_port\[\]/) {
$writer->startTag($tmpkey);
foreach my $ele ( sort @{$lynis_report_data{$key}} ) {
my ($port,$proto,$proc) = split(/\|/, $ele);
$writer->startTag('network_listen_port', 'protocol' => $proto, 'owner_process' => $proc);
$writer->characters($port);
$writer->endTag();
}
$writer->endTag();
}
when (/installed_packages_array/) {
$writer->startTag('installed_packages');
foreach my $ele ( sort @{$lynis_report_data{$key}} ) {
my ($name,$version) = split(/\,/, $ele);
$writer->emptyTag('installed_package', 'name' => $name, 'version' => $version);
}
$writer->endTag();
}
when (/details\[\]/) {
$writer->startTag('details');
foreach my $ele ( sort @{$lynis_report_data{$key}} ) {
my @parts = split(/\|/, $ele);
$writer->emptyTag('detail', 'id' => $parts[0], 'service' => $parts[1], 'description' => $parts[2]);
}
$writer->endTag();
}
when (/warning\[\]/) {
$writer->startTag('warnings');
foreach my $ele ( sort @{$lynis_report_data{$key}} ) {
my @parts = split(/\|/, $ele);
$writer->emptyTag('warning', 'id' => $parts[0], 'description' => $parts[1], 'severity' => $parts[2], 'f4' => $parts[3]);
}
$writer->endTag();
}
when (/suggestion\[\]/) {
$writer->startTag('suggestions');
foreach my $ele ( sort @{$lynis_report_data{$key}} ) {
my @parts = split(/\|/, $ele);
$writer->emptyTag('suggestion', 'id' => $parts[0], 'description' => $parts[1], 'severity' => $parts[2], 'f4' => $parts[3]);
}
$writer->endTag();
}
when (/real_user\[\]/) {
$writer->startTag('real_users');
foreach my $ele ( sort @{$lynis_report_data{$key}} ) {
my ($name,$uid) = split(/\,/, $ele);
$writer->startTag('real_user', 'uid' => $uid);
$writer->characters($name);
$writer->endTag();
}
$writer->endTag();
}
default {
$writer->startTag("${tmpkey}s");
foreach my $ele ( sort @{$lynis_report_data{$key}} ) {
$writer->dataElement($tmpkey, $ele);
}
$writer->endTag();
}
}
} else {
if ($key =~ /.*\[\]$/) {
$key =~ s/\[\]//g;
}
$writer->dataElement($key, $lynis_report_data{$key});
}
}
$writer->endTag('lynisReportData');
my $xml = $writer->end();
if ($output) {
$xmlout->close();
} else {
print $xml;
}
# XML is parsed directly from the report data array, using the XML::Writer module. So there should be no unhandled key-value pairs.
# So just undef the hash.
undef(%lynis_report_data);
} elsif ($excel) {
require Excel::Writer::XLSX;
my $i = 0;
# do the Excel thing....
my $wb = Excel::Writer::XLSX->new($output);
my $title_format = $wb->add_format( 'valign'=>'top', 'align'=>'left');
$title_format->set_size('32');
my $subtitle_format = $wb->add_format();
$subtitle_format->set_size('24');
my $subsub_format = $wb->add_format();
$subsub_format->set_size('16');
my $label_format = $wb->add_format('valign'=>'top','align'=>'left');
$label_format->set_bold();
my $version_format = $wb->add_format();
$version_format->set_num_format( '0.00' );
my $list_format = $wb->add_format('valign'=>'top', 'align'=>'left');
$list_format->set_text_wrap();
my $merge_format = $wb->add_format('valign'=>'top', 'align'=>'left');
my $spanhead_format = $wb->add_format('valign'=>'top','align'=>'center');
$spanhead_format->set_bold();
$spanhead_format->set_size('16');
### Summary Sheet Data
my $summary_ws = $wb->add_worksheet('Summary');
$summary_ws->merge_range('A1:C1', "lynis Asset Report", $title_format);
$summary_ws->write('A2', "Created by ");
$summary_ws->write_url('B2', "https://soltec.com", '', 'SOLTEC');
$summary_ws->write('A4', "Host Findings:", $subtitle_format);
$summary_ws->write('A5', "hardening index:", $label_format);
$summary_ws->write('B5', $lynis_report_data{'hardening_index'});
$summary_ws->write('C5', 'auditor:', $label_format);
$summary_ws->write('D5', $lynis_report_data{'auditor'});
my %params; my $last_row_number = 1; my @table_data;
if ((exists($lynis_report_data{'warning[]'})) and (ref($lynis_report_data{'warning[]'}) eq 'ARRAY')) {
$summary_ws->write('A7', "warnings \(".scalar(@{$lynis_report_data{'warning[]'}})."\):", $subsub_format);
#@header_row = [ 'Warning ID', 'Description', 'Severity', 'F4' ];
if ($lynis_report_data{'warning[]'}[0] =~ /\|/) {
foreach my $warn ( sort @{$lynis_report_data{'warning[]'}} ) {
my ($warn_id,$warn_desc,$warn_sev,$warn_f4) = split(/\|/, $warn);
push @table_data, [$warn_id,$warn_desc,$warn_sev,$warn_f4];
}
}
#print STDERR Dumper(\@table_data);
%params = (
'data' => \@table_data,
'header_row' => 1,
'autofilter' => 0,
'banded_columns' => 0,
'banded_rows' => 1,
'columns' => [
{ 'header' => 'Test ID' },
{ 'header' => 'Description' },
{ 'header' => 'Details' },
{ 'header' => 'Solution' },
]
);
#print STDERR Dumper(\%params);
$last_row_number = 8 + scalar(@table_data);
$summary_ws->add_table("A8:D$last_row_number", \%params);
} else {
$summary_ws->write('A7', "warnings (0):", $subsub_format);
}
@table_data = undef; my $next_row = 0;
if ((exists($lynis_report_data{'suggestion[]'})) and (ref($lynis_report_data{'suggestion[]'}) eq 'ARRAY')) {
$last_row_number++;
$next_row = $last_row_number;
$summary_ws->write("A${next_row}", "suggestions \(".scalar(@{$lynis_report_data{'suggestion[]'}})."\):", $subsub_format);
$next_row++;
#@header_row = [ 'Suggestion ID', 'Description', 'Severity', 'F4' ];
if ($lynis_report_data{'suggestion[]'}[0] =~ /\|/) {
foreach my $sugg (sort @{$lynis_report_data{'suggestion[]'}}) {
my ($sugg_id,$sugg_desc,$sugg_sev,$sugg_f4) = split(/\|/, $sugg);
push @table_data, [$sugg_id,$sugg_desc,$sugg_sev,$sugg_f4];
}
} else {
die(colored("\$lynis_report_data{'suggestion[]'}[0] does not contain a pipe (|).", "red"));
}
%params = (
'data' => \@table_data,
'header_row' => 1,
'autofilter' => 0,
'banded_columns' => 0,
'banded_rows' => 1,
'columns' => [
{ 'headet' => 'Test ID' },
{ 'header' => 'Description' },
{ 'header' => 'Details' },
{ 'header' => 'Solution' },
]
);
$last_row_number = $next_row + scalar(@table_data);
$summary_ws->add_table("A${next_row}:D${last_row_number}", \%params);
} else {
$summary_ws->write("A$next_row", "suggestions (0):", $subsub_format);
}
@table_data = undef;
$next_row = $last_row_number;
$next_row += 2;
if ((exists($lynis_report_data{'manual[]'})) and (ref($lynis_report_data{'manual[]'}) eq 'ARRAY')) {
$summary_ws->write("A${next_row}", "manual checks:", $subsub_format); $next_row++;
foreach my $mc ( sort @{$lynis_report_data{'manual[]'}} ) {
$summary_ws->write("A${next_row}", $mc, $merge_format);
$next_row++;
}
} else {
$summary_ws->write("A${next_row}", "manual checks (0):", $subsub_format); $next_row++;
}
$next_row += 2;
if (exists($lynis_report_data{'vulnerable_package[]'})) {
$summary_ws->write("A${next_row}", "vulnerable packages:", $subsub_format); $next_row++;
if (ref($lynis_report_data{'vulnerable_package[]'}) eq 'ARRAY') {
foreach my $vp ( sort @{$lynis_report_data{'vulnerable_package[]'}} ) {
$summary_ws->write("A${next_row}", $vp); $next_row++;
}
} else {
$summary_ws->write("A${next_row}", $lynis_report_data{'vulnerable_package[]'});
}
}
### lynis report data
my $lynis_ws = $wb->add_worksheet('lynis info');
$lynis_ws->merge_range('A1:D1', 'lynis info:', $title_format);
$lynis_ws->write('A2', 'lynis version:', $label_format); $lynis_ws->write('B2', $lynis_report_data{'lynis_version'}); $lynis_ws->write('C2', 'lynis tests done:', $label_format); $lynis_ws->write('D2', $lynis_report_data{'lynis_tests_done'});
$lynis_report_data{'lynis_update_available'} = 0 if ((defined($lynis_report_data{'lynis_update_available'})) and ($lynis_report_data{'lynis_update_available'} eq ""));
$lynis_ws->write('A3', 'lynis update available:', $label_format); $lynis_ws->write('B3', uc($to_bool{$lynis_report_data{'lynis_update_available'}})); $lynis_ws->write('C3', 'license key:', $label_format); $lynis_ws->write('D3', $lynis_report_data{'license_key'});
$lynis_ws->write('A4', 'report version:', $label_format); $lynis_ws->merge_range('B4:C4', "$lynis_report_data{'report_version_major'}\.$lynis_report_data{'report_version_minor'}", $version_format);
$lynis_ws->write('A5', "test category:", $label_format); $lynis_ws->write('B5', $lynis_report_data{'test_category'}); $lynis_ws->write('C5', 'test group:', $label_format); $lynis_ws->write('D5', $lynis_report_data{'test_group'});
$lynis_ws->write('A6', 'plugins enabled:', $label_format); $lynis_ws->write('B6', $lynis_report_data{'plugin_enabled[]'}); $lynis_ws->write('C6', 'plugin directory:', $label_format); $lynis_ws->write('D6', $lynis_report_data{'plugin_directory'});
$lynis_ws->write('A8', 'report start time:', $label_format); $lynis_ws->write('B8', $lynis_report_data{'report_datetime_start'}); $lynis_ws->write('C8', 'report end time:', $label_format); $lynis_ws->write('D8', $lynis_report_data{'report_datetime_end'});
$lynis_ws->write('A9', 'hostid1:', $label_format); $lynis_ws->merge_range('B9:D9', $lynis_report_data{'hostid'}, $merge_format);
$lynis_ws->write('A10', 'hostid2:', $label_format); $lynis_ws->merge_range('B10:D10', $lynis_report_data{'hostid2'}, $merge_format);
$lynis_ws->merge_range('A12:D12', 'plugin data:', $subtitle_format);
$i = 13;
if (exists($lynis_report_data{'plugin_enabled_phase1[]'})) {
$lynis_ws->write("A$i", "plugins enabled:", $subsub_format); $i++;
$lynis_ws->write("A$i", "name", $label_format); $lynis_ws->write("B$i", "version", $label_format); $i++;
if (ref($lynis_report_data{'plugin_enabled_phase1[]'}) eq 'ARRAY') {
foreach my $plug ( sort @{$lynis_report_data{'plugin_enabled_phase1[]'}} ) {
if ($plug =~ /\|/) {
my ($n, $v, $j) = split(/\|/, $plug);
$lynis_ws->write("A$i", $n); $lynis_ws->write("B$i", $v); $i++;
} else {
$lynis_ws->write("A$i", $plug); $i++;
}
}
}
}
$i++;
$lynis_ws->write("A$i", "plugin -> firewall:", $subsub_format); $i++;
$lynis_ws->write("A$i", 'iptables list:', $label_format); $i++;
if (exists($lynis_report_data{'plugin_firewall_iptables_list'})) {
if (ref($lynis_report_data{'plugin_firewall_iptables_list'}) eq 'ARRAY') {
foreach my $ipt ( sort @{$lynis_report_data{'plugin_firewall_iptables_list'}} ) {
$lynis_ws->write("A$i", $ipt); $i++;
}
} else {
$lynis_ws->write("A$i", $lynis_report_data{'plugin_firewall_iptables_list'}); $i++;
}
} else {
$lynis_ws->write("A$i", "N/A");
}
$i++;
$lynis_ws->merge_range("A$i:D$i", 'plugin -> processes:', $subsub_format); $i++;
$lynis_ws->merge_range("A$i:D$i", "all processes", $label_format); $i++;
if (exists($lynis_report_data{'plugin_processes_allprocesses'})) {
if (ref($lynis_report_data{'plugin_processes_allprocesses'}) eq 'ARRAY') {
foreach my $proc ( sort @{$lynis_report_data{'plugin_processes_allprocesses'}} ) {
$lynis_ws->merge_range("A$i:D$i", $proc, $merge_format); $i++;
}
} else {
$lynis_ws->merge_range("A$i:D$i", $lynis_report_data{'plugin_processes_allprocesses'}, $merge_format); $i++;
}
} else {
$lynis_ws->write("A$i", "N/A");
}
$i++;
### host infor
my $host_ws = $wb->add_worksheet('host info');
$host_ws->write('A1', "host info:", $title_format);
$host_ws->write('A2', 'hostname:', $label_format); $host_ws->write('B2', $lynis_report_data{'hostname'});
$host_ws->write('C2', 'domainname:', $label_format); $host_ws->write('D2', $lynis_report_data{'domainname'});
$host_ws->write('E2', 'resolv.conf domain:', $label_format); $host_ws->write('F2', $lynis_report_data{'resolv_conf_domain'});
$host_ws->write('A3', 'os:', $label_format); $host_ws->write('B3', $lynis_report_data{'os'});
$host_ws->write('C3', 'os fullname:', $label_format); $host_ws->write('D3', $lynis_report_data{'os_fullname'});
$host_ws->write('E3', 'os version:', $label_format); $host_ws->write('F3', $lynis_report_data{'os_version'});
$host_ws->write('A4', 'GRsecurity:', $label_format); $host_ws->write('B4', uc($to_bool{$lynis_report_data{'framework_grsecurity'}}));
$host_ws->write('C4', 'SELinux:', $label_format); $host_ws->write('D4', uc($to_bool{$lynis_report_data{'framework_selinux'}}));
$host_ws->write('E4', 'memory:', $label_format); $host_ws->write('F4', "$lynis_report_data{'memory_size'} $lynis_report_data{'memory_units'}");
$host_ws->write('A5', 'linux version:', $label_format); $host_ws->write('B5', $lynis_report_data{'linux_version'});
$host_ws->write('C5', 'PAE enabled:', $label_format); $host_ws->write('D5', uc($to_bool{$lynis_report_data{'cpu_pae'}}));
$host_ws->write('E5', 'NX enabled:', $label_format); $host_ws->write('F5', uc($to_bool{$lynis_report_data{'cpu_nx'}}));
$host_ws->write('A6', 'available shells:', $label_format); $host_ws->write('B6', join("\n", @{$lynis_report_data{'available_shell[]'}}), $list_format);
$host_ws->write('C6', 'locatedb:', $label_format); $host_ws->write('D6', $lynis_report_data{'locate_db'}, $merge_format); $host_ws->write('E6', 'uptime (days):', $label_format); $host_ws->write('F6', $lynis_report_data{'uptime_in_days'}, $merge_format);
$host_ws->write('A7', 'vm:', $label_format); $host_ws->write('B7', $vm_mode{$lynis_report_data{'vm'}});
$host_ws->write('C7', 'vm_type:', $label_format); $host_ws->write('D7', $lynis_report_data{'vmtype'});
$host_ws->write('E7', 'uptime(secs):', $label_format); $host_ws->write('F7', $lynis_report_data{'uptime_in_seconds'});
$lynis_report_data{'notebook'} = 0 if ((!exists($lynis_report_data{'notbook'})) or ($lynis_report_data{'notebook'} eq ''));
$host_ws->write('A8', 'is notebook/laptop:', $label_format); $host_ws->write('B8', uc($to_bool{$lynis_report_data{'notebook'}}));
$host_ws->write('C8', 'is container:', $label_format); $host_ws->write('D8', uc($to_bool{$lynis_report_data{'container'}}));
$host_ws->write('A9', 'binary paths:', $label_format); $host_ws->write('B9', $lynis_report_data{'binary_paths'});
$host_ws->write('C9', 'certificate count:', $label_format); $host_ws->write('D9', $lynis_report_data{'certificates'});
$host_ws->write('A10', 'authorized default USB devices:', $label_format); $host_ws->write('B10', join("\n", @{$lynis_report_data{'usb_authorized_default_device[]'}}), $list_format);
$host_ws->write('C10', 'certificates:', $label_format);
if (ref($lynis_report_data{'certificate[]'}) eq 'ARRAY') {
$host_ws->write('D10', join("\n", @{$lynis_report_data{'certificate[]'}}));
} else {
$host_ws->write('D10', $lynis_report_data{'certificate[]'});
}
$host_ws->write('C11', 'valid certificates:', $label_format);
if (ref($lynis_report_data{'valid_certificate[]'}) eq 'ARRAY') {
$host_ws->write('D11', join("\n", @{$lynis_report_data{'valid_certificate[]'}}));
} else {
$host_ws->write('D11', $lynis_report_data{'valid_certificate[]'});
}
$host_ws->write('C12', 'expired certificates:', $label_format);
if (ref($lynis_report_data{'expired_certificate[]'}) eq 'ARRAY') {
$host_ws->write('D12', join("\n", @{$lynis_report_data{'expired_certificate[]'}}));
} else {
$host_ws->write('D12', $lynis_report_data{'expired_certificate[]'});
}
$host_ws->write('A13', 'cron jobs:', $label_format);
$i = 14;
if ((exists($lynis_report_data{'cronjob[]'})) and (ref($lynis_report_data{'cronjob[]'}) eq 'ARRAY')) {
foreach my $job ( sort @{$lynis_report_data{'cronjob[]'}} ) {
$job =~ s/\,/ /g;
#$host_ws->write("A$i", $job);
$host_ws->merge_range("A$i:B$i", $job, $merge_format);
$i++;
}
} else {
$host_ws->write("A$i", $lynis_report_data{'cronjob[]'});
}
$i++;
$host_ws->write("A$i", "logging info:", $subsub_format); $i++;
$host_ws->write("A$i", "log rotation tool:", $label_format); $host_ws->write("B$i", $lynis_report_data{'log_rotation_tool'}); $host_ws->write("C$i", "log rotation config found:", $label_format); $host_ws->write("D$i", uc($to_bool{$lynis_report_data{'log_rotation_config_found'}}));
$i += 2; $host_ws->write("A$i", "log directories:", $subsub_format); $i++;
if ((exists($lynis_report_data{'log_directory[]'})) and (ref($lynis_report_data{'log_directory[]'}) eq 'ARRAY')) {
foreach my $dir ( sort @{$lynis_report_data{'log_directory[]'}} ) {
$host_ws->merge_range("A$i:C$i", $dir, $merge_format); $i++;
}
} else {
$host_ws->merge_range("A$i:C$i", $lynis_report_data{'log_directory[]'}, $merge_format);
}
$i++; $host_ws->write("A$i", "open log files:", $subsub_format); $i++;
if ((exists($lynis_report_data{'open_logfile[]'})) and (ref($lynis_report_data{'open_logfile[]'}) eq 'ARRAY')) {
foreach my $f ( sort @{$lynis_report_data{'open_logfile[]'}} ) {
$host_ws->merge_range("A$i:C$i", $f, $merge_format); $i++;
}
} else {
$host_ws->merge_range("A$i:C$i", $lynis_report_data{'open_logfile[]'}, $merge_format);
}
$i++; $host_ws->write("A$i", "open empty log files:", $subsub_format); $i++;
if ((exists($lynis_report_data{'open_empty_log_file[]'})) and (ref($lynis_report_data{'open_empty_log_file[]'}) eq 'ARRAY')) {
foreach my $f ( sort @{$lynis_report_data{'open_empty_log_file[]'}} ) {
$host_ws->merge_range("A$i:C$i", $f, $merge_format); $i++;
}
} else {
$host_ws->merge_range("A$i:C$i", $lynis_report_data{'open_empty_log_file[]'}, $merge_format);
}
### network infdo
my $net_ws = $wb->add_worksheet('network info');
$net_ws->write('A1', "network info:", $title_format);
$net_ws->write('A2', 'ipv6 mode:', $label_format); $net_ws->write('B2', $lynis_report_data{'ipv6_mode'});
$net_ws->write('C2', "ipv6 only:", $label_format); $net_ws->write('D2', uc($to_bool{$lynis_report_data{'ipv6_only'}}));
$net_ws->write('A3', 'network interfaces:', $label_format); $net_ws->write('B3', join("\r\n", @{$lynis_report_data{'network_interface[]'}}));
if (ref($lynis_report_data{'localhost-mapped-to'}) eq 'ARRAY') {
$net_ws->write('C3', 'localhost mapped to:', $label_format); $net_ws->write('D3', join("\r\n", @{$lynis_report_data{'localhost-mapped-to'}}));
} else {
$net_ws->write('C3', 'localhost mapped to:', $label_format); $net_ws->write('D3', $lynis_report_data{'localhost-mapped-to'});
}
$net_ws->write('A4', 'ipv4 addresses', $label_format); $net_ws->write('B4', join("\r\n", @{$lynis_report_data{"network_ipv4_address[]"}}));
$net_ws->write('A5', 'ipv6 addresses', $label_format); $net_ws->write('B5', join("\r\n", @{$lynis_report_data{"network_ipv6_address[]"}}));
$net_ws->write('A6', 'default gateway', $label_format); $net_ws->write('B6', $lynis_report_data{'default_gateway[]'});
$net_ws->write('A7', 'mac addresses', $label_format);
if (exists($lynis_report_data{'network_mac_address[]'})) {
if (ref($lynis_report_data{'network_mac_address[]'}) eq 'ARRAY') {
$net_ws->write('B7', join("\n", @{$lynis_report_data{'network_mac_address[]'}}));
} else {
$net_ws->write('B7', $lynis_report_data{'network_mac_address[]'});
}
} else {
$net_ws->write('B7', "N/A");
}
$net_ws->write('C7', 'name cache used:', $label_format); $net_ws->write('D7', uc($to_bool{$lynis_report_data{'name_cache_used'}}), $merge_format);
$net_ws->write('A8', 'name servers:', $label_format);
if (exists($lynis_report_data{'nameserver[]'})) {
if (ref($lynis_report_data{'name_server[]'}) eq 'ARRAY') {
$net_ws->write('B8', join("\n", @{$lynis_report_data{'nameserver[]'}}), $merge_format);
} else {
$net_ws->write('B8', $lynis_report_data{'nameserver[]'}, $merge_format);
}
} else {
$net_ws->write('B8', "N/A");
}
$net_ws->write('A9', 'resolv.conf search domain', $label_format); $net_ws->write('B9', $lynis_report_data{'resolv_conf_search_domain[]'});
$net_ws->write('A11', 'open ports:', $subsub_format);
if ((exists($lynis_report_data{'network_listen_port[]'})) and (ref($lynis_report_data{'network_listen_port[]'}) eq 'ARRAY')) {
$net_ws->write('A12', "ip address", $label_format); $net_ws->write('B12', 'port', $label_format); $net_ws->write('C12', 'protocol', $label_format); $net_ws->write('D12', 'daemon/process', $label_format); $net_ws->write('E12', '???', $label_format);
$i = 13;
foreach my $rec ( sort @{$lynis_report_data{'network_listen_port[]'}} ) {
#print STDERR colored("$rec \n", "bold magenta");
my ($ipp,$pr,$d,$u) = split(/\|/, $rec);
my ($ip, $port);
if (grep(/\:/, split(//, $ipp)) > 1) {
my @parts = split(/\:/, $ipp);
$port = pop(@parts); # gets the last element of the array. like $parts[-1];
$ip = join(":", @parts); # should only be the remaining parts, which should be the ipv6 addr
} else {
# must be IPv4
($ip,$port) = split(/\:/, $ipp);
}
$net_ws->write("A$i", $ip); $net_ws->write("B$i", $port); $net_ws->write("C$i", $pr); $net_ws->write("D$i", $d); $net_ws->write("E$i", $u);
$i++;
}
} else {
warn colored("network_listen_port[] not an array!", "yellow");
}
### security info
my $sec_ws = $wb->add_worksheet('security info');
$sec_ws->write('A1', "security info:", $title_format);
$sec_ws->write('A2', 'host firewall installed:', $label_format); $sec_ws->write('B2', uc($to_bool{$lynis_report_data{'firewall_installed'}}));
$sec_ws->write('C2', 'firewall software:', $label_format); $sec_ws->write('D2', $lynis_report_data{'firewall_software[]'});
$sec_ws->write('E2', 'firewall empty ruleset:', $label_format); $sec_ws->write('F2', uc($to_bool{$lynis_report_data{'firewall_empty_ruleset'}}));
$sec_ws->write('G2', 'firewall active:', $label_format); $sec_ws->write('H2', uc($to_bool{$lynis_report_data{'firewall_active'}}));
$sec_ws->write('A3', 'package audit tool found', $label_format); $sec_ws->write('B3', uc($to_bool{$lynis_report_data{'package_audit_tool_found'}}));
$sec_ws->write('C3', 'package audit tool;', $label_format); $sec_ws->write('D3', $lynis_report_data{'package_audit_tool'});
$sec_ws->write('E3', 'vulnerable packages found:', $label_format); $sec_ws->write('F3', uc($to_bool{$lynis_report_data{'vulnerable_packages_found'}}));
$sec_ws->write('G3', 'package manager:', $label_format); $sec_ws->write('H3', $lynis_report_data{'package_manager[]'});
$sec_ws->write('A4', 'two-factor authentication enabled:', $label_format); $sec_ws->write('B4', uc($to_bool{$lynis_report_data{'authentication_two_factor_enabled'}}));
$sec_ws->write('C4', 'two-factor authentication required:', $label_format); $sec_ws->write('D4', uc($to_bool{$lynis_report_data{'authentication_two_factor_required'}}));
$sec_ws->write('E4', 'LDAP PAM module enabled:', $label_format); $sec_ws->write('F4', uc($to_bool{$lynis_report_data{'ldap_pam_enabled'}}));
$sec_ws->write('G4', 'LDAP authentication enabled:', $label_format); $sec_ws->write('H4', uc($to_bool{$lynis_report_data{'ldap_auth_enabled'}}));
$sec_ws->write('A5', 'minimum password length:', $label_format); $sec_ws->write('B5', $lynis_report_data{'minimum_password_length'});
$sec_ws->write('C5', 'maximum password days:', $label_format); $sec_ws->write('D5', $lynis_report_data{'password_max_days'});
$sec_ws->write('E5', 'minimum password days:', $label_format); $sec_ws->write('F5', $lynis_report_data{'password_min_days'});
$sec_ws->write('G5', 'maximum password retries:', $label_format); $sec_ws->write('H5', $lynis_report_data{'max_password_retry'});
$sec_ws->write('A6', 'password complexity score:', $label_format); $sec_ws->write_formula('B6', "=DEC2BIN($pass_score)");
$sec_ws->write('C6', 'PAM cracklib found:', $label_format); $sec_ws->write('D6', uc($to_bool{$lynis_report_data{'pam_cracklib'}}));
$sec_ws->write('E6', 'password strength tested:', $label_format); $sec_ws->write('F6', uc($to_bool{$lynis_report_data{'password_strength_tested'}}));
$sec_ws->write('G6', 'PAM password quality:', $label_format); $sec_ws->write('H6', $lynis_report_data{'pam_pwquality'});
if (ref($lynis_report_data{'pam_auth_brute_force_protection_module'}) eq 'ARRAY') {
$sec_ws->write('A7', 'PAM brute force protection module:', $label_format); $sec_ws->write('B7', join("\n", @{$lynis_report_data{'pam_auth_brute_force_protection_module[]'}}));
} else {
$sec_ws->write('A7', 'PAM brute force protection module:', $label_format); $sec_ws->write('B7', $lynis_report_data{'pam_auth_brute_force_protection_module[]'});
}
$sec_ws->write('C7', 'failed logins logged:', $label_format); $sec_ws->write('D7', uc($to_bool{$lynis_report_data{'auth_failed_logins_logged'}}));
$sec_ws->write('E7', 'apparmor enabled:', $label_format); $sec_ws->write('F7', uc($to_bool{$lynis_report_data{'apparmor_enabled'}}));
$sec_ws->write('G7', 'apparmor policy loaded:', $label_format); $sec_ws->write('H7', uc($to_bool{$lynis_report_data{'apparmor_policy_loaded'}}));
$sec_ws->write('A8', 'authentication brute force protection:', $label_format); $sec_ws->write('B8', uc($to_bool{$lynis_report_data{'authentication_brute_force_protection'}}));
$sec_ws->write('A8', 'file integrity tool installed:', $label_format); $sec_ws->write('B8', uc($to_bool{$lynis_report_data{'file_integrity_tool_installed'}}));
$sec_ws->write('C8', 'file integreity tool(s):', $label_format); $sec_ws->write('D8', $lynis_report_data{'file_integrity_tool[]'});
$sec_ws->write('E8', 'automation tool present:', $label_format); $sec_ws->write('F8', uc($to_bool{$lynis_report_data{'automation_tool_present'}}));
$sec_ws->write('G8', 'automation tool(s):', $label_format); $sec_ws->write('H8', $lynis_report_data{'automation_tool_running[]'});
$sec_ws->write('A9', 'malware scanner installed', $label_format); $sec_ws->write('B9', uc($to_bool{$lynis_report_data{'malware_scanner_installed'}}));
$sec_ws->write('C9', 'malware scanner(s):', $label_format); $sec_ws->write('D9', $lynis_report_data{'malware_scanner[]'});
$sec_ws->write('E9', 'compiler installed:', $label_format); $sec_ws->write('F9', uc($to_bool{$lynis_report_data{'compiler_installed'}}));
$sec_ws->write('G9', 'compiler(s):', $label_format); $sec_ws->write('H9', $lynis_report_data{'compiler[]'});
$sec_ws->write('A10', 'IDS/IPS tooling', $label_format);
if (exists($lynis_report_data{'ids_ips_tooling'})) {
if (ref($lynis_report_data{'ids_ips_tooling'}) eq 'ARRAY') {
$sec_ws->write('B10', join("\n", @{$lynis_report_data{'ids_ips_tooling'}}), $merge_format);
} else {
$sec_ws->write('B10', $lynis_report_data{'ids_ips_tooling'}, $merge_format);
}
} else {
$sec_ws->write('B10', 'NA', $merge_format);
}
if (exists($lynis_report_data{'fail2ban_config'})) {
$sec_ws->write('C10', 'fail2ban config file(s):', $label_format);
$sec_ws->write('D10', $lynis_report_data{'fail2ban_config'}, $merge_format);
} else {
$sec_ws->write('D10', 'NA', $merge_format);
}
if (exists($lynis_report_data{'fail2ban_enabled_service[]'})) {
$sec_ws->write('E10', 'fail2ban enabled service(s):', $label_format);
if (ref($lynis_report_data{'fail2ban_enabled_service[]'}) eq 'ARRAY') {
$sec_ws->write('F10', join("\n", @{$lynis_report_data{'fail2ban_enabled_service[]'}}), $merge_format);
} else {
$sec_ws->write('F10', $lynis_report_data{'fail2ban_enabled_service[]'}, $merge_format);
}
}
$sec_ws->write("G10", "session timeout enabled:", $label_format);
$sec_ws->write("H10", uc($to_bool{$lynis_report_data{'session_timeout_enabled'}}));
$sec_ws->merge_range('A12:B12', 'real users:', $subsub_format); $sec_ws->merge_range('C12:D12', 'home directories:', $subsub_format);
$sec_ws->write('A13', 'name', $label_format); $sec_ws->write('B13', 'uid', $label_format);
$i = 14;
if ((exists($lynis_report_data{'real_user[]'})) and (ref($lynis_report_data{'real_user[]'}) eq 'ARRAY')) {
foreach my $usr ( sort @{$lynis_report_data{'real_user[]'}} ) {
my ($n, $uid) = split(/\,/, $usr);
$sec_ws->write("A$i", $n); $sec_ws->write("B$i", $uid); $i++;
}
} else {
warn colored("real_user[] not found or not an array!", "yellow");
print STDERR color('yellow');
print STDERR ref($lynis_report_data{'real_user[]'})."\n";
print STDERR Dumper($lynis_report_data{'real_user[]'});
print STDERR color('reset');
}
$i = 13;
if ((exists($lynis_report_data{'home_directory[]'})) and (ref($lynis_report_data{'home_directory[]'}) eq 'ARRAY')) {
foreach my $dir ( sort @{$lynis_report_data{'home_directory[]'}} ) {
$sec_ws->write("C$i", $dir); $i++;
}
} else {
warn colored("home_directory[] not found or not an array!", "yellow");
print STDERR color("yellow");
print STDERR ref($lynis_report_data{'home_directory[]'})."\n";
print STDERR Dumper($lynis_report_data{'home_directory[]'});
print STDERR color('reset');
}
$i++;
$sec_ws->write("A$i", "PAM modules:", $subsub_format); $i++;
if ((exists($lynis_report_data{'pam_module[]'})) and (ref($lynis_report_data{'pam_module[]'}) eq 'ARRAY')) {
foreach my $mod ( sort @{$lynis_report_data{'pam_module[]'}} ) {
$sec_ws->write("A$i", $mod); $i++;
}
} else {
warn colored("pam_module[] not found or not an array!", "yellow");
print STDERR color("yellow");
print STDERR ref($lynis_report_data{'pam_module[]'})."\n";
print STDERR Dumper($lynis_report_data{'pam_module[]'});
print STDERR color('reset');
}
### boot info
my $boot_ws = $wb->add_worksheet('boot info');
$boot_ws->write('A1', "boot info:", $title_format);
$boot_ws->write('A2', 'UEFI booted:', $label_format); $boot_ws->write('B2', uc($to_bool{$lynis_report_data{'boot_uefi_booted'}}));
$boot_ws->write('C2', 'UEFI booted secure:', $label_format); $boot_ws->write('D2', uc($to_bool{$lynis_report_data{'boot_uefi_booted_secure'}}));
$boot_ws->write('E2', 'boot loader:', $label_format); $boot_ws->write('F2', $lynis_report_data{'boot_loader'});
$boot_ws->write('A3', 'default runlevel:', $label_format); $boot_ws->write('B3', $lynis_report_data{'linux_default_runlevel'});
$boot_ws->write('C3', 'boot service tool:', $label_format); $boot_ws->write('D3', $lynis_report_data{'boot_service_tool'});
$i = 5;
if (exists($lynis_report_data{'boot_service[]'})) {
$boot_ws->write("A$i", "services started at boot:", $subsub_format); $i++;
if (ref($lynis_report_data{'boot_service[]'}) eq 'ARRAY') {
foreach my $bs ( sort @{$lynis_report_data{'boot_service[]'}} ) {
$boot_ws->write("A$i", $bs); $i++;
}
} else {
$boot_ws->write("A$i", $lynis_report_data{'boot_service[]'});
}
}
### kernel inso
my $kernel_ws = $wb->add_worksheet('kernel info');
$kernel_ws->write('A1', "kernel info:", $title_format);
$kernel_ws->write('A2', "kernel version:", $label_format);
$kernel_ws->write('B2', $lynis_report_data{'linux_kernel_version'});
$kernel_ws->write('C2', 'full kernel version:', $label_format);
$kernel_ws->write('D2', $lynis_report_data{'os_kernel_version_full'});
$kernel_ws->write('A3', 'kernel release version:', $label_format);
$kernel_ws->write('B3', $lynis_report_data{'linux_kernel_release'});
$kernel_ws->write('C3', 'kernel IO scheduler:', $label_format);
if (exists($lynis_report_data{'linux_kernel_io_scheduler[]'})) {
if (ref($lynis_report_data{'linux_kernel_io_scheduler[]'}) eq 'ARRAY') {
$kernel_ws->write('D3', join("\n", @{$lynis_report_data{'linux_kernel_io_scheduler[]'}}));
} else {
$kernel_ws->write('D3', $lynis_report_data{'linux_kernel_io_scheduler[]'});
}
} else {
$kernel_ws->write('D3', 'N/A');
}
$kernel_ws->write('A4', 'linux kernel type:', $label_format);
$kernel_ws->write('B4', $lynis_report_data{'linux_kernel_type'});
$kernel_ws->write('C4', 'number of kernels available:', $label_format);
$kernel_ws->write('D4', $lynis_report_data{'linux_amount_of_kernels'});
$kernel_ws->write('A5', 'linux (kernel) config file:', $label_format);
$kernel_ws->write('B5', $lynis_report_data{'linux_config_file'});
$i = 7;
if (exists($lynis_report_data{'loaded_kernel_module[]'})) {
$kernel_ws->write("A$i", "loaded kernel modules:", $subsub_format); $i++;
if (ref($lynis_report_data{'loaded_kernel_module[]'}) eq 'ARRAY') {
foreach my $mod ( sort @{$lynis_report_data{'loaded_kernel_module[]'}} ) {
$kernel_ws->write("A$i", $mod); $i++;
}
} else {
$kernel_ws->write("A$i", $lynis_report_data{'loaded_kernel_module[]'});
}
}
### filesystem/journalling info
my $fs_ws = $wb->add_worksheet('filesystem info');
$fs_ws->write('A1', "filesystem info:", $title_format);
$fs_ws->write('A2', "journal disk size:", $label_format); $fs_ws->write('B2', $lynis_report_data{'journal_disk_size'});
$fs_ws->write('A3', "most recent journal coredump:", $label_format); $fs_ws->write('B3', $lynis_report_data{'journal_coredump_lastday'});
$fs_ws->write('A4', 'oldest boot date on journal:', $label_format); $fs_ws->write('B4', $lynis_report_data{'journal_oldest_bootdate'});
$fs_ws->write('A5', 'journal contains errors:', $label_format); $fs_ws->write('B5', uc($to_bool{$lynis_report_data{'journal_contains_errors'}}));
$fs_ws->write('A6', 'journal boot logging enabled:', $label_format); $fs_ws->write('B6', uc($to_bool{$lynis_report_data{'journal_bootlogs'}}));
$fs_ws->write("C2", 'swap partitions:', $label_format);
if (exists($lynis_report_data{'swap_partition[]'})) {
if (ref($lynis_report_data{'swap_partition[]'}) eq 'ARRAY') {
$fs_ws->write("D2", join("\n", $lynis_report_data{'swap_partition[]'}));
} else {
$lynis_report_data{'swap_partition[]'} =~ s/,/\n/g;
$fs_ws->write("D2", $lynis_report_data{'swap_partition[]'});
}
} else {
$fs_ws->write("D2", 'N/A');
}
$fs_ws->write('C3', "LVM volume group(s):", $label_format);
if (exists($lynis_report_data{'lvm_volume_group[]'})) {
if (ref($lynis_report_data{'lvm_volume_group[]'}) eq 'ARRAY') {
$fs_ws->write("D3", join("\n", @{$lynis_report_data{'lvm_volume_group[]'}}));
} else {
$lynis_report_data{'lvm_volume_group[]'} =~ s/,/\n/g;
$fs_ws->write("D3", $lynis_report_data{'lvm_volume_group[]'});
}
} else {
$fs_ws->write('D3', 'N/A');
}
$fs_ws->write('C4', 'LVM volume(s):', $label_format);
if (exists($lynis_report_data{'lvm_volume[]'})) {
if (ref($lynis_report_data{'lvm_volume[]'}) eq 'ARRAY') {
$fs_ws->write("D4", join("\n", @{$lynis_report_data{'lvm_volume[]'}}));
} else {
$lynis_report_data{'lvm_volume[]'} =~ s/,/\n/g;
$fs_ws->write('D4', $lynis_report_data{'lvm_volume[]'});
}
} else {
$fs_ws->write("D4", "N/A");
}
$fs_ws->write("C5", "ext filesystems:", $label_format);
if (exists($lynis_report_data{'file_systems_ext[]'})) {
if (ref($lynis_report_data{'file_systems_ext[]'}) eq 'ARRAY') {
$fs_ws->write("D5", join("\n", @{$lynis_report_data{'file_systems_ext[]'}}));
} else {
$lynis_report_data{'file_systems_ext[]'} =~ s/,/\n/g;
$fs_ws->write("D5", $lynis_report_data{'file_systems_ext[]'});
}
} else {
$fs_ws->write("D5", "N/A");
}
$i = 8;
if (exists($lynis_report_data{'journal_meta_data'})) {
$fs_ws->merge_range("A$i:B$i", 'journal metadata:', $subsub_format); $i++;
if (ref($lynis_report_data{'journal_meta_data'}) eq 'ARRAY') {
foreach my $r ( @{$lynis_report_data{'journal_meta_data'}} ) {
$fs_ws->merge_range("A$i:B$i", $r, $merge_format); $i++;
}
} else {
$fs_ws->merge_range("A$i:B$i", $lynis_report_data{'journal_meta_data'}, $merge_format); $i++;
}
}
if (exists($lynis_report_data{'deleted_file[]'})) {
$fs_ws->write("A$i", 'deleted files still on the filesystem:', $subsub_format); $i++;
if (ref($lynis_report_data{'deleted_file[]'}) eq 'ARRAY') {
foreach my $df ( sort @{$lynis_report_data{'deleted_file[]'}} ) {
$fs_ws->write("A$i", $df); $i++;
}
} else {
$fs_ws->write("A$i", $lynis_report_data{'deleted_file[]'});
}
}
### service info
my $svc_ws = $wb->add_worksheet('service info');
$svc_ws->write('A1', "service info:", $title_format);
$i = 3;
foreach my $prog ( sort qw( redis ntp_daemon mysql ssh_daemon dhcp_client arpwatch audit_daemon postgresql linux_auditd nginx ) ) {
if ((!defined($lynis_report_data{"${prog}_running"})) or ($lynis_report_data{"${prog}_running"} eq "")) {
$lynis_report_data{"${prog}_running"} = 0;
}
$svc_ws->write("A$i", "$prog running:", $label_format); $svc_ws->write("B$i", uc($to_bool{$lynis_report_data{"${prog}_running"}}));
$i++;
}
my $i_hold = $i; # $i should be 13
$i = 3;
$svc_ws->write("C$i", "imap daemon:", $label_format); $svc_ws->write("D$i", $lynis_report_data{"imap_daemon"}); $i++;
$svc_ws->write("C$i", "ntp daemon:", $label_format); $svc_ws->write("D$i", $lynis_report_data{"ntp_daemon"}); $i++;
$svc_ws->write("C$i", "pop3 daemon:", $label_format); $svc_ws->write("D$i", $lynis_report_data{"pop3_daemon"}); $i++;
$svc_ws->write("C$i", "printing daemon", $label_format); $svc_ws->write("D$i", $lynis_report_data{"printing_daemon"}); $i++;
$svc_ws->write("C$i", "running service tool:", $label_format); $svc_ws->write("D$i", $lynis_report_data{"running_service_tool"}); $i++;
if ((exists($lynis_report_data{'scheduler[]'})) and (ref($lynis_report_data{'scheduler[]'}) eq 'ARRAY')) {
$svc_ws->write("C$i", "scheduler(s):", $label_format); $svc_ws->write("D$i", join("\n", @{$lynis_report_data{"scheduler[]"}})); $i++;
} else {
$svc_ws->write("C$i", "scheduler(s):", $label_format); $svc_ws->write("D$i", $lynis_report_data{"scheduler[]"}); $i++;
}
$svc_ws->write("C$i", "service manager:", $label_format); $svc_ws->write("D$i", $lynis_report_data{"service_manager"}); $i++;
$svc_ws->write("C$i", "smtp daemon:", $label_format); $svc_ws->write("D$i", $lynis_report_data{"smtp_daemon"}); $i++;
$svc_ws->write("C$i", "systemctl exit code:", $label_format); $svc_ws->write("D$i", $lynis_report_data{'systemctl_exit_code'}); $i = 3;
$svc_ws->write("E$i", "syslog daemon present:", $label_format); $svc_ws->write("F$i", uc($to_bool{$lynis_report_data{'syslog_daemon_present'}})); $i++;
$svc_ws->write("E$i", "syslog daemon(s):", $label_format); $svc_ws->write("F$i", join("\n", @{$lynis_report_data{'syslog_daemon[]'}})); $i++;
#if ($i > $i_hold) { $i_hold = $i; } # $i should be 11, so this should never actually be true
#$i = $i_hold; $i++; # reset to 13 and add 1 (14)
# Just manually reset to 14, since we added the new column.
$i = 14;
#$i += 2;
$svc_ws->merge_range("A$i:D$i", "ntp detail", $spanhead_format); $i++;
$svc_ws->write("A$i", "ntp config found:", $label_format); $svc_ws->write("B$i", uc($to_bool{$lynis_report_data{'ntp_config_found'}}));
$svc_ws->write("C$i", 'ntp config file:', $label_format); $svc_ws->write("D$i", $lynis_report_data{'ntp_config_file'}); $i++;
$svc_ws->write("A$i", 'ntp version:', $label_format); $svc_ws->write("B$i", $lynis_report_data{'ntp_version'});
$svc_ws->write("C$i", 'ntp unreliable peers:', $label_format);
if ((exists($lynis_report_data{'ntp_unrealiable_peer[]'})) and (ref($lynis_report_data{'ntp_unreliable_peer[]'}) eq 'ARRAY')) {
$svc_ws->write("D$i", join("\n", @{$lynis_report_data{'ntp_unrealible_peer[]'}}));
} else {
$svc_ws->write("D$i", $lynis_report_data{'ntp_unreliable_peer[]'});
}
$i++;
$svc_ws->write("A$i", "ntp config type:", $label_format);
if ($lynis_report_data{'ntp_config_type_startup'}) {
$svc_ws->write("B$i", "startup");
} elsif ($lynis_report_data{'ntp_config_type_eventbased'}) {
$svc_ws->write("B$i", "eventbased");
} elsif ($lynis_report_data{'ntp_config_type_daemon'}) {
$svc_ws->write("B$i", "daemon");
} elsif ($lynis_report_data{'ntp_config_type_scheduled'}) {
$svc_ws->write("B$i", "scheduled");
} else {
$svc_ws->write("B$i", "unrecognized");
}
$i += 2;
$svc_ws->merge_range("A$i:D$i", "Apache detail", $spanhead_format); $i++;
$svc_ws->write("A$i", 'apache_version:', $label_format); $svc_ws->write("B$i", $lynis_report_data{'apache_version'});
$svc_ws->write("C$i", 'apache modules:', $label_format);
if (ref($lynis_report_data{'apache_module[]'}) eq 'ARRAY') {
$svc_ws->write("D$i", join("\r\n", @{$lynis_report_data{'apache_module[]'}}), $list_format); $i++;
} else {
$svc_ws->write("D$i", $lynis_report_data{'apache_module[]'}, $list_format); $i++;
}
$i++;
$svc_ws->merge_range("A$i:D$i", "nginx detail", $spanhead_format); $i++;
$svc_ws->write("A$i", 'nginx main config file:', $label_format); $svc_ws->write("B$i", $lynis_report_data{'nginx_main_conf_file'});
if (ref($lynis_report_data{'nginx_sub_conf_file'}) eq 'ARRAY') {
$svc_ws->write("C$i", 'nginx sub config files:', $label_format); $svc_ws->write("D$i", join("\r\n", @{$lynis_report_data{'nginx_sub_conf_file[]'}}), $list_format); $i++;
} else {
$svc_ws->write("C$i", 'nginx sub config files:', $label_format); $svc_ws->write("D$i", $lynis_report_data{'nginx_sub_conf_file[]'}, $list_format); $i++;
}
if (ref($lynis_report_data{'log_file'}) eq 'ARRAY') {
$svc_ws->write("A$i", 'nginx log files:', $label_format); $svc_ws->write("B$i", join("\r\n", @{$lynis_report_data{'log_file'}}), $list_format);
} else {
$svc_ws->write("A$i", 'nginx log files:', $label_format); $svc_ws->write("B$i", $lynis_report_data{'log_file'}, $list_format);
}
if (ref($lynis_report_data{'ssl_tls_protocol_enabled[]'}) eq 'ARRAY') {
$svc_ws->write("C$i", 'SSL/TLS protocols enabled:', $label_format); $svc_ws->write("D$i", join("\r\n", @{$lynis_report_data{'ssl_tls_protocol_enabled[]'}}), $list_format); $i++;
} else {
$svc_ws->write("C$i", 'SSL/TLS protocols enabled:', $label_format); $svc_ws->write("D$i", $lynis_report_data{'ssl_tls_protocol_enabled[]'}, $list_format); $i++;
}
$svc_ws->write("A$i", 'nginx config options:', $label_format);
if (ref($lynis_report_data{'nginx_config_option[]'}) eq 'ARRAY') {
foreach my $opt ( @{$lynis_report_data{'nginx_config_option[]'}} ) {
$svc_ws->write("B$i", $opt); $i++;
}
} else {
$svc_ws->write("B$i", $lynis_report_data{'nginx_config_option[]'}); $i++;
}
$i++; # give it a row for space
$svc_ws->merge_range("A$i:D$i", "systemd detail", $spanhead_format); $i++;
$svc_ws->write("A$i", "systemd enabled:", $label_format); $svc_ws->write("B$i", uc($to_bool{$lynis_report_data{'systemd'}}));
$svc_ws->write("C$i", "systemd status:", $label_format); $svc_ws->write("D$i", $lynis_report_data{'systemd_status'}); $i++;
$svc_ws->write("A$i", "systemd built-in components:", $label_format); $svc_ws->merge_range("B$i:D$i", $lynis_report_data{'systemd_builtin_components'}, $merge_format); $i++;
$svc_ws->write("A$i", "systemd version:", $label_format); $svc_ws->write("B$i", $lynis_report_data{'systemd_version'}); $i++;
$i_hold = $i; # reset the ($i_hold) bar. All lists below start at his row level
$svc_ws->write("A$i", "running services:", $subsub_format); $i++;
if ((exists($lynis_report_data{'running_service[]'})) and (ref($lynis_report_data{'running_service[]'}) eq 'ARRAY')) {
foreach my $svc ( sort @{$lynis_report_data{'running_service[]'}} ) {
$svc_ws->write("A$i", $svc); $i++;
}
} else {
warn colored("running_service[] array not found or not an array!", "yellow");
print STDERR color("yellow");
print STDERR Dumper($lynis_report_data{'running_service[]'});
print STDERR color('reset');
}
$i = $i_hold;
$svc_ws->write("B$i", "systemd services not found:", $subsub_format); $i++;
if ((exists($lynis_report_data{'systemd_service_not_found[]'})) and (ref($lynis_report_data{'systemd_service_not_found[]'}) eq 'ARRAY')) {
foreach my $svc ( sort @{$lynis_report_data{'systemd_service_not_found[]'}} ) {
$svc_ws->write("B$i", $svc); $i++;
}
} else {
warn colored("systemd_service_not_found[] array not found or not an array!", "yellow");
print STDERR color("yellow");
print STDERR Dumper($lynis_report_data{'systemd_service_not_found[]'});
print STDERR color('reset');
}
$i = $i_hold;
$svc_ws->merge_range("C$i:D$i", "systemd unit files:", $subsub_format); $i++;
$svc_ws->write("C$i", "unit", $label_format); $svc_ws->write("D$i", "status", $label_format); $i++;
if (ref($lynis_report_data{'systemd_unit_file[]'}) eq 'ARRAY') {
foreach my $svc ( sort @{$lynis_report_data{'systemd_unit_file[]'}} ) {
chomp($svc);
my ($s, $st, @j) = split(/\|/, $svc);
$svc_ws->write("C$i", $s); $svc_ws->write("D$i", $st); $i++;
}
} else {
warn colored("systemd_unit_file[] not an array!", "yellow");
print STDERR color("yellow");
print STDERR Dumper($lynis_report_data{'systemd_unit_file[]'});
print STDERR color('reset');
}
$i = $i_hold;
$svc_ws->write("E$i", "systemd unit not found:", $subsub_format); $i++;
if (ref($lynis_report_data{'systemd_unit_not_found[]'}) eq 'ARRAY') {
foreach my $svc ( sort @{$lynis_report_data{'systemd_unit_not_found[]'}} ) {
$svc_ws->write("E$i", $svc); $i++;
}
} else {
warn colored("systemd_unit_not_found[] not an array!", "yellow");
print STDERR color("yellow");
print STDERR Dumper($lynis_report_data{'systemd_unit_not_found[]'});
print STDERR color('reset');
}
$i++;
### package info
my $pkg_ws = $wb->add_worksheet('package info');
$pkg_ws->write('A1', "package info:", $title_format);
$pkg_ws->write('A2', "number of packages installed:", $label_format); $pkg_ws->write('B2', $lynis_report_data{'installed_packages'}); $pkg_ws->write('C2', 'number of binaries found:', $label_format); $pkg_ws->write('D2', $lynis_report_data{'binaries_count'});
$pkg_ws->merge_range('A4:D4', 'installed packages:', $subsub_format);
#$pkg_ws->merge_range('A5:B5', 'name', $label_format); $pkg_ws->merge_range('C5:D5', 'version', $label_format);
$i = 5;
foreach my $p ( sort @{$lynis_report_data{'installed_packages_array'}} ) {
chomp($p);
#my ($name, $ver) = split(/(?:\,|\-)/, $p);
#$pkg_ws->merge_range("A$i:B$i", $name, $merge_format); $pkg_ws->merge_range("C$i:D$i", $ver, $merge_format);
$pkg_ws->merge_range("A$i:D$i", $p, $merge_format);
$i++;
}
### Handled indeces for Excel format.
my @indexes = qw( lynis_version lynis_tests_done license_key report_version test_category test_group installed_packages binaries_count installed_packages_array report_datetime_start report_datetime_end hostid hostid2 hostname domainname resolv_conf_domain resolv_conf_search_domain[] os os_fullname os_version framework_grsecurity framework_selinux memory_size memory_units cpu_pae cpu_nx linux_version vm uptime_in_seconds uptime_in_days locate_db available_shell[] binary_paths open_empty_log_file[] os_kernel_version os_kernel_version_full file_integrity_tool boot_uefi_booted password_max_other_credit scheduler[] ids_ips_tooling[] malware_scanner_installed redis_running auditor journal_disk_size journal_coredumps_lastday journal_oldest_bootdate journal_contais_errors jounal_bootlogs );
my @idx2 = qw( cronjob[] log_rotation_tool log_directory[] log_rotation_config_found network_ipv4_address[] network_ipv6_address[] network_interface[] ipv6_mode ipv6_only warning[] suggestion[] network_listen_port[] usb_authorized_default_device[] network_mac_address[] default_gateway[] os_name lynis_update_available hardening_index plugin_directory plugins_enabled notebook open_logfile[] report_version_major report_version_minor valid_certificate[] min_password_class home_directory[] name_cache_used automation_tool_running[] real_user[] ntp_config_type_startup ntp_config_type_eventbased ntp_config_type_daemon ntp_config_type_scheduled ntp_version ntp_unreliable_peer[] ntp_config_file[] ntp_config_found redis_running linux_kernel_io_scheduler[] finish journal_meta_data );
my @idx3 = qw( firewall_installed firewall_software[] firewall_empty_ruleset firewall_active package_audit_tool_found package_audit_tool vulnerable_packages_found package_manager[] authentication_two_factor_enabled authentication_two_factor_required ldap_oam_enabled ldap_auth_enabled minimum_password_length password_max_days password_min_days max_password_retry pam_cracklib password_strength_tested auth_failed_logins_logged password_max_u_credit password_max_l_credit password_max_o_credit ldap_pam_enabled running_service[] pam_module[] nameserver[] password_max_digital_credit massword_max_other_credit swap_partition[] linux_kernel_io_scheduler firewall_software journal_bootlogs linux_config_file linux_auditd_running lvm_volume_group[] lvm_volume[] filesystems_ext[] manual[] );
my @idx4 = qw( compiler_installed compiler[] ids_ips_tooling file_integrity_tool_installed file_integrity_tool[] automation_tool_present automation_tool_installed[] malware_scanner installed malware_scanner[] fail2ban_config fail2ban_enabled_service[] loaded_kernel_module[] linux_default_runlevel boot_service_tool boot_urfi_booted boot_uefi_booted_secure boot_service[] linux_kernel_scheduler[] linux_amount_of_kernels linux_kernel_type linux_kernel_release linux_kernel_version os_kernel_version_full systemd_service_not_found[] systemd_unit_file[] systemd_unit_not_found[] ssh_daemon_running postgresql_running mysql_running audit_daemon_running crond_running arpwatch_running ntp_daemon_running nginx_running dhcp_client_running ntp_daemon printing_daemon pop3_daemon smtp_daemon imap_daemon );
my @idx5 = qw( session_timeout_enabled details[] deleted_file[] file_systems_ext[] journal_contains_errors vulnerable_package[] boot_loader systemd systemd_status systemd_builtin_components service_manager systemd_version running_service_tool systemctl_exit_code plugin_firewall_iptables_list systemctl_exit_code plugin_processes_allprocesses vmtype plugin_enabled_phase1[] syslog_daemon_present syslog_daemon[] valid_certificate[] certificate[] certificates apparmor_enabled apparmor_policy_loaded pam_auth_brute_force_protection_module[] authentication_brute_force_protection container pam_pwquality localhost-mapped-to apache_version apache_module[] expired_certificate[] nginx_main_conf_file nginx_sub_conf_file[] log_file ssl_tls_protocol_enabled nginx_config[] ssl_tls_protocol_enabled[] nginx_config_option[] );
push @indexes, @idx2, @idx3, @idx4, @idx5;
foreach my $idx ( sort @indexes ) {
delete($lynis_report_data{$idx});
}
### unknown tab
### capture any remaining fields that we haven't handled yet.
my $unk_ws = $wb->add_worksheet('unknown fields');
$i = 1;
foreach my $k ( sort keys %lynis_report_data ) {
$unk_ws->write("A$i", $k, $label_format); $unk_ws->write("B$i", $lynis_report_data{$k}); $i++;
}
} else {
open OUT, ">$htmldoc" or die colored("There was a problem opening the output file ($htmldoc): $!", "bold red");
print OUT <<END;
<!DOCTYPE HTML>
<html lang="en">
<head>
<title>lynis report</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css">
html,body {color: #fff; background-color: #000;}
h3#exceptions {color: #ff0000;}
div#content_section {margin: 0 10% 0 10%;}
div.content_subsection {margin: 0 5% 0 5%;}
div.collapsable {display:none;}
div#footer {width:60%;margin:0 auto 0 20%;}
select {background:transparent;color:#fff;}
table {border-collapse:collapse;border:1px solid gray;}
table.list {border-collapse:collapse;border:none;}
table.list td,th {border-collapse:collapse;border:none;}
table#lynis_plugins_table {width:100%;}
table#scoreauditor {border-collapse:collapse;border:none;width:90%;}
td {padding:2px 5px 2px 5px;vertical-align:top;border:1px solid gray;}
td.field_label {font-size:1.1em;font-weight:bold;color:#555;}
td.good {background-color: #006400; color: #fff; font-weight: bold;}
td.fair {background-color: #ffd700; color: #000; font-weight: bold;}
td.poor {background-color: #ffa500; color: #000; font-weight: bold;}
td.dismal {background-color: #ff0000; color: #000; font-weight: bold;}
td.tf_bad {background-color:#ff0000; color: #000; font-weight: bold;}
td.tf_good {background-color: #006400; color: #fff; font-weight: bold;}
td#score {vertical-align:top;text-align:left;}
td#auditor {vertical-align:top;text-align:right;}
span.title_shrink {font-size: 75%;}
a:link#github_link {color: #fff;}
a:visited#github_link {color: #acacac;}
a:hover#github_link {color: #0000ff;}
a:active#github_link {color:#000;}
a:link {color: #fff;}
a:visited {color: #555;}
a:hover {color: #0000ff;}
a:active {color:#000;}
</style>
<script type="text/javascript">
function toggle(link,content) {
var ele = document.getElementById(content);
var text = document.getElementById(link);
if (ele.style.display == "block" ) {
ele.style.display = "none";
text.innerHTML = "&gt;&nbsp;show&nbsp;&lt;";
} else {
ele.style.display = "block";
text.innerHTML = "&lt;&nbsp;hide&nbsp;&gt;";
}
}
</script>
</head>
<body>
<div id="content_section">
<h1>lynis Asset Report</h1>
<h2><span class="title_shrink">created by</span> <a id="github_link" href="http://github.com/d4t4king/lynis_report" target="_blank">lynis_report</a></h2>
<table>
<tr>
<td><a href="#lynis_info">lynis info</a></td><td><a href="#host_info">host info</a></td>
<td><a href="#network_info">network info</a></td><td><a href="#security_info">security Info</a></td>
<td><a href="#boot_info">boot info</a></td><td><a href="#kernel_info">kernel info</a></td>
</tr>
<tr>
<td><a href="#filesystem_info">filesystem/journalling info</a></td><td><a href="#service_info">service info</a></td>
<td><a href="#installed_packages">installed packages</a></td><td></td>
<td></td><td></td>
</tr>
</table>
<hr />
<h3>host findings:</h3>
<table class="list" id="scoreauditor"><tr><td id="score"><table><tr><td>hardening index:</td>
END
given ($lynis_report_data{'hardening_index'}) {
when (($lynis_report_data{'hardening_index'} < 100) and ($lynis_report_data{'hardening_index'} > 90)) {
# green
print OUT "\t\t\t\t<td class=\"good\">$lynis_report_data{'hardening_index'}</td>";
}
when (($lynis_report_data{'hardening_index'} <= 90) and ($lynis_report_data{'hardening_index'} > 80)) {
# yellow
print OUT "\t\t\t\t<td class=\"fair\">$lynis_report_data{'hardening_index'}</td>";
}
when (($lynis_report_data{'hardening_index'} <= 80) and ($lynis_report_data{'hardening_index'} > 65)) {
# orange
print OUT "\t\t\t\t<td class=\"poor\">$lynis_report_data{'hardening_index'}</td>";
}
when ($lynis_report_data{'hardening_index'} <= 65) {
# red
print OUT "\t\t\t\t<td class=\"dismal\">$lynis_report_data{'hardening_index'}</td>";
}
default {
# error
}
}
print OUT "\t\t\t</tr></table></td><td><table><tr><td id=\"auditor\">Auditor:</td><td>$lynis_report_data{'auditor'}</td></tr></table></td></tr></table>\n";
# handle "exception events"
if (exists($lynis_report_data{'exception_event[]'})) {
print OUT "<h3 id=\"exceptions\">exceptions!</h3>\n";
print OUT "<div class=\"content_subsection\">\n";
print OUT "<div> There were exceptions found on this system. This means that there is something drastically wrong with the system OR lynis doesn't quite know how to handle what it found.</div><br />\n";
if (ref($lynis_report_data{'exception_event[]'}) eq 'ARRAY') {
print OUT "<table border=\"1\">\n";
foreach my $exp ( @{$lynis_report_data{'exception_event[]'}} ) {
print OUT "<tr><td>$exp</td></tr>\n";
}
print OUT "</table>\n</div>\n";
} else {
print OUT "<table border=\"1\"><tr><td>$lynis_report_data{'exception_event[]'}</td></tr></table>\n";
}
}
# warnings
if (!exists($lynis_report_data{'warning[]'})) {
print OUT "<h4>warnings (0):</h4>\n";
} else {
print OUT "<h4>warnings (".scalar(@{$lynis_report_data{'warning[]'}})."):</h4>\n";
}
print OUT <<END;
<div class="content_subsection">
<table>
<tr><td>Test ID</td><td>Description</td><td>Details</td><td>Solution</td></tr>
END
if (exists($lynis_report_data{'warning[]'})) {
if (ref($lynis_report_data{'warning[]'}) eq 'ARRAY') {
if (${$lynis_report_data{'warning[]'}}[0] =~ /\|/) { # more than one
foreach my $warn ( sort @{$lynis_report_data{'warning[]'}} ) {
my ($warn_id,$warn_desc,$warn_sev,$warn_f4) = split(/\|/, $warn);
print OUT "\t\t\t\t\t<tr><td>$warn_id</td><td>$warn_desc</td><td>$to_long_severity{$warn_sev}</td><td>$warn_f4</td></tr>\n";
}
} elsif (${$lynis_report_data{'warning[]'}}[0] =~ /[A-Z]{4}\-\d{4}/) { # one warning
print colored(Dumper(\@{$lynis_report_data{'warning[]'}})."\n", "green") if ($verbose);
my $warn_id = ${$lynis_report_data{'warning[]'}}[0];
my $warn_desc = ${$lynis_report_data{'warning[]'}}[1];
my $warn_sev = ${$lynis_report_data{'warning[]'}}[2];
my $warn_f4 = ${$lynis_report_data{'warning[]'}}[3];
print OUT "\t\t\t\t\t<tr><td>$warn_id</td><td>$warn_desc</td><td>$to_long_severity{$warn_sev}</td><td>$warn_f4</td></tr>\n";
} else {
die colored("Unexpected ARRAY format!\n".Dumper(\@{$lynis_report_data{'warning[]'}}), "bold red");
}
} else {
die colored("warning[] not ARRAY ref: ".ref($lynis_report_data{'warning[]'}), "bold red");
}
}
print OUT <<END;
</table>
</div>
END
if ((ref($lynis_report_data{'suggestion[]'}) eq 'ARRAY') and
(${$lynis_report_data{'suggestion[]'}}[0] =~ /\|/)) {
print OUT "\t\t\t<h4>suggestions (".scalar(@{$lynis_report_data{'suggestion[]'}})."):</h4>\n";
} else {
print OUT "\t\t\t<h4>suggestions (0):</h4>\n";
}
print OUT <<END;
<div class="content_subsection">
<table>
<tr><td>Test ID</td><td>Description</td><td>Details</td><td>Solution</td></tr>
END
if ((ref($lynis_report_data{'suggestion[]'}) eq 'ARRAY') and
(${$lynis_report_data{'suggestion[]'}}[0] =~ /\|/)) {
foreach my $sug ( sort @{$lynis_report_data{'suggestion[]'}} ) {
my ($sug_id,$sug_desc,$sug_sev,$sug_f4,$sug_f5) = split(/\|/, $sug);
if ($sug_desc eq 'Consider hardening SSH configuration') {
$sug_desc .= ": $sug_sev"; $sug_sev = '-';
}
print OUT "\t\t\t\t\t<tr><td>$sug_id</td>";
print OUT "<td>$sug_desc</td>";
print OUT "<td>".($sug_sev ? $sug_sev : "&nbsp;")."</td>";
print OUT "<td>".($sug_f4 ? $sug_f4 : "&nbsp;")."</td></tr>\n";
}
}
print OUT <<END;
</table>
<h4>manual checks:</h4>
<ul>
END
if ((exists($lynis_report_data{'manual[]'})) and (scalar(@{$lynis_report_data{'manual[]'}}) > 0)) {
foreach my $man ( sort @{$lynis_report_data{'manual[]'}} ) {
#print Dumper($man);
chomp($man);
print OUT "\t\t\t\t\t<li>$man</li>\n";
}
}
print OUT <<END;
</ul><br />
END
if ((exists($lynis_report_data{'deleted_file[]'})) and ($lynis_report_data{'deleted_file[]'} ne "")) {
if (ref($lynis_report_data{'deleted_file[]'}) eq 'ARRAY') {
print OUT "\t\t\t\t<h5>deleted files (".scalar(@{$lynis_report_data{'deleted_file[]'}})."):</h5>\n";
if (scalar(@{$lynis_report_data{'deleted_file[]'}}) < 10) {
print OUT "\t\t\t\t<select size=\"".scalar(@{$lynis_report_data{'deleted_file[]'}})."\" name=\"lbDeletedFiles\">\n";
} else {
print OUT "\t\t\t\t<select size=\"10\" name=\"lbDeletedFiles\">\n";
}
foreach my $f ( @{$lynis_report_data{'deleted_file[]'}} ) { print OUT "\t\t\t\t\t<option>$f\n"; }
} else {
if (($verbose) and ($verbose > 1)) {
warn colored("Deleted files object not an array! ", "yellow");
print Dumper($lynis_report_data{'delete_file[]'});
}
}
}
print OUT "\t\t\t\t</select><br />\n";
if ((exists($lynis_report_data{'vulnerable_package[]'})) and ($lynis_report_data{'vulnerable_package[]'} ne "")) {
if (ref($lynis_report_data{'vulnerable_package[]'}) eq 'ARRAY') {
print OUT "\t\t\t\t<h4>Vulnerable packages (".scalar(@{$lynis_report_data{'vulnerable_package[]'}})."):\n";
print OUT "\t\t\t\t<ul>\n";
foreach my $p ( @{$lynis_report_data{'vulnerable_package[]'}} ) { print OUT "\t\t\t\t\t<li>$p</li>\n"; }
print OUT "\t\t\t\t</ul><br />\n";
} else {
if ((($verbose) and ($verbose > 1)) or ($debug)) {
warn colored("Vulnerable package object not an array!", "yellow");
print color('yellow'); print Dumper($lynis_report_data{'vulnerable_package[]'}); print color('reset');
}
}
}
# It's easier to move stuff around if there is one cell (or cell group) per libe for the tables. Maybe this
# isn't ideal HTML writing, but it makes sense when writing the tool.
$lynis_report_data{'lynis_update_available'} = 0 if ((!defined($lynis_report_data{'lynis_update_available'})) or ($lynis_report_data{'lynis_update_available'} eq ""));
print OUT <<END;
</div>
<hr />
<!--
###############################
### LYNIS INFO
###############################
-->
<h3><a id="lynis_info">lynis info:</a></h3>
<div class="content_subsection">
<table>
<tr>
END
print OUT "\t\t\t\t\t<td class=\"field_label\">lynis version:</td><td>$lynis_report_data{'lynis_version'}</td>\n";
print OUT "\t\t\t\t\t<td class=\"field_label\">lynis tests done:</td><td>$lynis_report_data{'lynis_tests_done'}</td>\n";
print OUT <<END;
</tr>
<tr>
END
if (($lynis_report_data{'lynis_update_available'} == 0) or ($lynis_report_data{'lynis_update_available'} == 1)) {
print OUT "\t\t\t\t\t<td class=\"field_label\">lynis update available:</td><td>$to_bool{$lynis_report_data{'lynis_update_available'}}</td>\n";
} elsif ($lynis_report_data{'lynis_update_available'} == -1) {
print OUT "\t\t\t\t\t<td class=\"field_label\">lynis update available:</td><td>N/A - There was an unexpected error trying to retrieve update status.</td>\n";
} else {
warn colored("Unexpected result from lynis update available check!", "yellow");
print Dumper($lynis_report_data{'lynis_update_available'});
}
print OUT "\n\n\n\n\n\n<td class=\"field_label\">license key:</td><td>$lynis_report_data{'license_key'}</td>\n";
print OUT <<END;
</tr>
<tr>
<td colspan="2" class="field_label">report version:</td><td colspan="2">$lynis_report_data{'report_version_major'}.$lynis_report_data{'report_version_minor'}</td>
</tr>
<tr>
<td class="field_label">test category:</td><td>$lynis_report_data{'test_category'}</td>
<td class="field_label">test group:</td><td>$lynis_report_data{'test_group'}</td>
</tr>
<tr>
<td class="field_label">number of plugins enabled:</td><td>$lynis_report_data{'plugins_enabled'}</td>
<td class="field_label">plugin directory:</td><td>$lynis_report_data{'plugin_directory'}</td>
</tr>
<tr>
END
print OUT "\t\t\t\t\t\t<td class=\"field_label\">phase 1 plugins enabled:</td><td colspan=\"3\">\n";
print OUT "\t\t\t\t\t\t\t<table id=\"lynis_plugins_table\">\n";
if (exists($lynis_report_data{'plugin_enabled_phase1[]'})) {
if (ref($lynis_report_data{'plugin_enabled_phase1[]'}) eq 'ARRAY') {
foreach my $plug ( sort @{$lynis_report_data{'plugin_enabled_phase1[]'}} ) {
my ($n,$v) = split(/\|/, $plug);
if ((!defined($v)) or ($v eq "")) { $v = "AAAAAAAA"; }
print OUT "\t\t\t\t\t\t\t\t<tr><td>name:</td><td>$n</td><td>version:</td><td>$v</td></tr>\n";
}
}
}
print OUT "\t\t\t\t\t\t\t</table>\n";
print OUT "\t\t\t\t\t\t</td>\n";
print OUT <<END;
</tr>
<tr>
<td class="field_label">report start time:</td><td>$lynis_report_data{'report_datetime_start'}</td><td>report end time:</td><td>$lynis_report_data{'report_datetime_end'}</td>
</tr>
<tr><td class="field_label">hostid:</td><td colspan="3">$lynis_report_data{'hostid'}</td></tr>
<tr><td class="field_label">hostid:</td><td colspan="3">$lynis_report_data{'hostid2'}</td></tr>
END
if (ref($lynis_report_data{'plugin_firewall_iptables_list'}) eq 'ARRAY') {
print OUT "\t\t\t\t\t<tr><td class=\"field_label\">Plugin-firewall iptables list:</td><td colspan=\"3\">".join("<br />\n", @{$lynis_report_data{'plugin_firewall_iptables_list'}})."</td></tr>\n";
}
print OUT "\t\t\t\t</table>\n";
if ((exists($lynis_report_data{'plugin_processes_allprocesses'})) and ($lynis_report_data{'plugin_processes_allprocesses'} ne "")) {
print OUT "\t\t\t\t<h5>Plugin-processes: discovered processes:</h5>\n";
if (ref($lynis_report_data{'plugin_processes_allprocesses'}) eq 'ARRAY') {
if (scalar(@{$lynis_report_data{'plugin_processes_allprocesses'}}) < 10) {
print OUT "\t\t\t\t\t\t<select size=\"".scalar(@{$lynis_report_data{'plugin_processes_allprocesses'}})."\" name=\"lbPluginProcessesAllProcesses\" >\n";
} else {
print OUT "\t\t\t\t\t<select size=\"10\" name=\"lbPluginProcessesAllProcesses\" >\n";
}
foreach my $p ( sort @{$lynis_report_data{'plugin_processes_allprocesses'}} ) { print OUT "\t\t\t\t\t\t<option>$p\n"; }
print OUT "\t\t\t\t\t</select>\n";
} else {
if ((($verbose) and ($verbose > 1)) or ($debug)) {
warn colored("plugin processess allprocesses object not an array! ", "yellow");
print Dumper($lynis_report_data{'plugin_processes_allprocesses'});
}
}
}
###########################
### HOST INFO
###########################
print OUT <<END;
</div>
<hr />
<h3><a id="host_info">host info:</a></h3>
<div class="content_subsection">
<table>
<tr>
<td class="field_label">hostname:</td><td>$lynis_report_data{'hostname'}</td>
<td class="field_label">domainname:</td><td>$lynis_report_data{'domainname'}</td>
END
if ((defined($lynis_report_data{'resolv_conf_domain'})) and ($lynis_report_data{'resolv_conf_domain'} ne "")) {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">resolv.conf domain:</td><td>$lynis_report_data{'resolv_conf_domain'}</td>\n";
} else {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">resolv.conf domain:</td><td>&nbsp;</td>\n";
}
print OUT <<END;
</tr>
<tr>
<td class="field_label">os:</td><td>$lynis_report_data{'os'}</td>
<td class="field_label">os fullname:</td><td>$lynis_report_data{'os_fullname'}</td>
<td class="field_label">os_version:</td><td>$lynis_report_data{'os_version'}</td>
</tr>
<tr>
<td class="field_label">GRSecurity:</td><td>$to_bool{$lynis_report_data{'framework_grsecurity'}}</td>
<td class="field_label">SELinux:</td><td>$to_bool{$lynis_report_data{'framework_selinux'}}</td>
<td class="field_label">memory:</td><td>$lynis_report_data{'memory_size'} $lynis_report_data{'memory_units'}</td>
</tr>
<tr>
<td class="field_label">linux version:</td><td>$lynis_report_data{'linux_version'}</td>
<td class="field_label">pae enabled:</td><td>$to_bool{$lynis_report_data{'cpu_pae'}}</td>
<td class="field_label">nx enabled:</td><td>$to_bool{$lynis_report_data{'cpu_nx'}}</td>
</tr>
<tr>
END
if (exists($lynis_report_data{'available_shell[]'})) {
if (ref($lynis_report_data{'available_shell[]'}) eq 'ARRAY') {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">Available shells:</td><td>".join("<br />\n", @{$lynis_report_data{'available_shell[]'}})."</td>\n";
} else {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">Available shells:</td><td>$lynis_report_data{'available_shell[]'}</td>\n";
}
} else {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">Available shells:</td><td>&nbsp;</td>\n";
}
$lynis_report_data{'locate_db'} = "&nbsp;" if ((!defined($lynis_report_data{'locate_db'})) or ($lynis_report_data{'locate_db'} eq ""));
#print STDERR colored($lynis_report_data{'vm'}."\n", "bold magenta");
$lynis_report_data{'vm'} = 0 if ((!defined($lynis_report_data{'vm'})) or ($lynis_report_data{'vm'} eq ""));
#print STDERR colored($lynis_report_data{'vm'}."\n", "bold magenta");
print OUT "\t\t\t\t\t<td class=\"field_label\">locate db:</td><td>$lynis_report_data{'locate_db'}</td>\n";
print OUT "\t\t\t\t\t<td class=\"field_label\">uptime (days):</td><td>$lynis_report_data{'uptime_in_days'}</td>\n";
print OUT <<END;
</tr>
<tr>
<td class="field_label">vm:</td><td>$vm_mode{$lynis_report_data{'vm'}}</td>
END
if ((defined($lynis_report_data{'vmtype'})) and ($lynis_report_data{'vmtype'} ne "")) {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">vm_type:</td><td>$lynis_report_data{'vmtype'}</td>\n";
} else {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">vm_type:</td><td>&nbsp;</td>\n";
}
print OUT <<END;
<td class="field_label">uptime (secs):</td><td>$lynis_report_data{'uptime_in_seconds'}</td>
</tr>
END
print OUT "<tr><td class=\"field_label\">is notebook/laptop:</td><td colspan=\"2\">$to_bool{$lynis_report_data{'notebook'}}</td>";
print OUT "<td class=\"field_label\">is Docker container:</td><td colspan=\"2\">$to_bool{$lynis_report_data{'container'}}</td></tr>\n";
print OUT <<END;
<tr>
<td class="field_label">binary paths:</td><td colspan="2">$lynis_report_data{'binary_paths'}</td>
END
if (ref($lynis_report_data{'valid_certificate[]'}) eq 'ARRAY') {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">valid certificates:</td><td colspan=\"2\">".join("<br />\n",@{$lynis_report_data{'valid_certificate[]'}})."</td>\n";
} else {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">valid certificates:</td><td colspan=\"2\">$lynis_report_data{'valid_certificate[]'}</td>\n";
}
print OUT <<END;
</tr>
<tr>
END
if (ref($lynis_report_data{'usb_authorized_default_device[]'}) eq 'ARRAY') {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">authorized default USB devices:</td><td colspan=\"2\">".join("<br \>\n", @{$lynis_report_data{'usb_authorized_default_device[]'}})."</td>\n";
} else {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">authorized default USB devices:</td><td colspan=\"2\">$lynis_report_data{'usb_authorized_default_device[]'}</td>\n";
}
if (ref($lynis_report_data{'expired_certificate[]'}) eq 'ARRAY') {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">expired certificates:</td><td colspan=\"2\">".join("<br />\n", @{$lynis_report_data{'expired_certificate[]'}})."</td>\n";
} else {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">expired certificates:</td><td colspan=\"2\">$lynis_report_data{'expired_certificate[]'}</td>\n";
}
print OUT <<END;
</tr>
<tr>
END
#if ($verbose) { print colored("Contents of \$lynis_report_data\{\'certificates\'\}:\n".Dumper($lynis_report_data{'certificates'}), "yellow"); }
print OUT "\t\t\t\t\t\t<td class=\"field_label\">certificate count:</td><td colspan=\"2\">$lynis_report_data{'certificates'}</td>\n";
if (ref($lynis_report_data{'certificate[]'}) eq 'ARRAY') {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">certificates:</td><td colspan=\"2\">".join("<br />\n", @{$lynis_report_data{'certificate[]'}})."</td>\n";
} else {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">certificates:</td><td colspan=\"2\">$lynis_report_data{'certificate[]'}</td>\n";
}
print OUT <<END;
</tr>
<tr>
END
if (exists($lynis_report_data{'compiler_world_executable[]'})) {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">world executable compiler(s):</td>";
if (ref($lynis_report_data{'compiler_world_executable[]'}) eq 'ARRAY') {
print OUT "\t\t\t\t\t\t<td colspan=\"2\">".join("<br />\n". @{$lynis_report_data{'compiler_world_executable[]'}})."</td>\n";
} else {
print OUT "\t\t\t\t\t\t<td colspan=\"2\">$lynis_report_data{'compiler_world_executable[]'}</td>\n";
}
} else {
print OUT <<END;
<td class="field_label"></td><td colspan="2"></td>
END
}
print OUT <<END;
<td class="field_label"></td><td colspan="2"></td>
</tr>
</table>
<h4>cron jobs:</h4>
END
if (ref($lynis_report_data{'cronjob[]'}) eq "ARRAY") {
print OUT "\t\t\t\t\t<select size=\"10\" name=\"lbCronJobs\">\n";
foreach my $c ( @{$lynis_report_data{'cronjob[]'}} ) {
$c =~ s/,/\t&nbsp;/g;
print OUT "\t\t\t\t\t\t<option>$c\n";
}
print OUT "\t\t\t\t\t</select>\n";
}
print OUT <<END;
<h4>logging info:</h4>
<table>
<tr>
<td class="field_label">log rotation tool:</td><td>$lynis_report_data{'log_rotation_tool'}</td>
<td class="field_label">log rotation config found:</td><td>$to_bool{$lynis_report_data{'log_rotation_config_found'}}</td>
</tr>
END
if (ref($lynis_report_data{'syslog_daemon_present'}) eq 'ARRAY') {
my $i = 0;
foreach my $e ( @{$lynis_report_data{'syslog_daemon_present'}} ) { $i += $e; }
if ($i >= 1) {
print OUT "<tr><td class=\"field_label\"colspan=\"2\">syslog daemon detected:</td><td colspan=\"2\">$to_bool{1}</td></tr>\n";
} else {
print OUT "<tr><td class=\"field_label\"colspan=\"2\">syslog daemon detected:</td><td colspan=\"2\">$to_bool{0}</td></tr>\n";
}
} else {
print OUT "<tr><td class=\"field_label\"colspan=\"2\">syslog daemon detected:</td><td colspan=\"2\">$to_bool{$lynis_report_data{'syslog_daemon_present'}}</td></tr>\n";
}
print OUT <<END;
<tr>
<td class="field_label" colspan="2">syslog daemon(s):</td>
END
if (ref($lynis_report_data{'syslog_daemon[]'}) eq 'ARRAY') {
print OUT "\t\t\t\t\t\t<td colspan=\"2\">".join("<br />\n", @{$lynis_report_data{'syslog_daemon[]'}})."</td>\n";
} else {
print OUT "\t\t\t\t\t\t<td colspan=\"2\">$lynis_report_data{'syslog_daemon[]'}</td>\n";
}
print OUT "\t\t\t\t\t</tr>\n";
print OUT <<END;
</table>
<br />
<h4>log directories:</h4>
END
if (ref($lynis_report_data{'log_directory[]'}) eq 'ARRAY') {
if (scalar(@{$lynis_report_data{'log_directory[]'}}) < 10) {
print OUT "\t\t\t\t\t<select size=\"".scalar(@{$lynis_report_data{'log_directory[]'}})."\" name=\"lbLogDirectories\">\n";
} else {
print OUT "\t\t\t\t\t<select size=\"10\" name=\"lbLogDirectories\">\n";
}
foreach my $ld ( @{$lynis_report_data{'log_directory[]'}} ) { print OUT "\t\t\t\t\t\t<option>$ld\n"; }
print OUT "\t\t\t\t\t</select>\n";
}
print OUT "\t\t\t\t\t<h4>open log files:</h4>\n";
if (ref($lynis_report_data{'open_logfile[]'}) eq 'ARRAY') {
if (scalar(@{$lynis_report_data{'open_logfile[]'}}) < 10) {
print OUT "\t\t\t\t\t<select size=\"".scalar(@{$lynis_report_data{'open_logfile[]'}})."\" name=\"blOpenLogFiles\">\n";
} else {
print OUT "\t\t\t\t\t<select size=\"10\" name=\"blOpenLogFiles\">\n";
}
foreach my $lf ( @{$lynis_report_data{'open_logfile[]'}} ) { print OUT "\t\t\t\t\t\t<option>$lf\n"; }
print OUT "\t\t\t\t\t</select>\n";
}
print OUT "\t\t\t\t<h4>open empty log files:</h4>\n";
if (ref($lynis_report_data{'open_empty_log_file[]'}) eq 'ARRAY') {
if (scalar(@{$lynis_report_data{'open_empty_log_file[]'}}) < 10) {
print OUT "\t\t\t\t\t<select size=\"".scalar(@{$lynis_report_data{'open_empty_log_file[]'}})."\" name=\"blOpenEmptyLogFiles\">\n";
} else {
print OUT "\t\t\t\t\t<select size=\"10\" name=\"blOpenEmptyLogFIles\">\n";
}
foreach my $elf ( @{$lynis_report_data{'open_empty_log_file[]'}} ) { print OUT "\t\t\t\t\t\t<option>$elf\n"; }
print OUT "\t\t\t\t\t</select>\n";
}
################################
### NETWORK INFO
################################
print OUT <<END;
</div>
<hr />
<h3><a id="network_info">network info:</a></h3>
<div class="content_subsection">
<table>
<tr>
<td class="field_label">IPv6 Mode:</td><td>$lynis_report_data{'ipv6_mode'}</td>
<td class="field_label">IPv6 Only:</td><td>$to_bool{$lynis_report_data{'ipv6_only'}}</td>
</tr>
END
if (exists($lynis_report_data{'network_interface[]'})) {
if (ref($lynis_report_data{'network_interface[]'}) eq 'ARRAY') {
print OUT "\t\t\t\t\t<tr><td class=\"field_label\" colspan=\"2\">network interfaces:</td><td colspan=\"2\">".join("<br />\n", @{$lynis_report_data{'network_interface[]'}})."</td></tr>\n";
} else {
print OUT "\t\t\t\t\t\t<tr><td class=\"field_label\" colspan=\"2\">network interfaces:</td><td colspan=\"2\">$lynis_report_data{'network_interface[]'}</td></tr>\n";
}
} else {
print OUT "\t\t\t\t\t\t<tr><td class=\"field_label\" colspan=\"2\">network interfaces:</td><td colspan=\"2\">&nbsp;</td></tr>\n";
}
print OUT "\t\t\t\t\t\t<tr><td class=\"field_label\" colspan=\"2\">localhost mapped to:</td><td colspan=\"2\">$lynis_report_data{'localhost-mapped-to'}</td></tr>\n";
if (exists($lynis_report_data{'network_ipv4_address[]'})) {
if (ref($lynis_report_data{'network_ipv4_address[]'}) eq 'ARRAY') {
print OUT "\t\t\t\t\t<tr><td class=\"field_label\" colspan=\"2\">ipv4 addresses:</td><td colspan=\"2\">".join("<br />\n", @{$lynis_report_data{'network_ipv4_address[]'}})."</td></tr>\n";
} else {
print OUT "\t\t\t\t\t<tr><td class=\"field_label\" colspan=\"2\">ipv4 addresses:</td><td colspan=\"2\">$lynis_report_data{'network_ipv4_address[]'}</td></tr>\n";
}
} else {
print OUT "\t\t\t\t\t<tr><td class=\"field_label\" colspan=\"2\">ipv4 addresses:</td><td colspan=\"2\">&nbsp;</td></tr>\n";
}
if (exists($lynis_report_data{'network_ipv6_address[]'})) {
if (ref($lynis_report_data{'network_ipv6_address[]'}) eq 'ARRAY') {
print OUT "\t\t\t\t\t<tr><td class=\"field_label\" colspan=\"2\">ipv6 addresses:</td><td colspan=\"2\">".join("<br />\n", @{$lynis_report_data{'network_ipv6_address[]'}})."</td></tr>\n";
} else {
print OUT "\t\t\t\t\t<tr><td class=\"field_label\" colspan=\"2\">ipv6 addresses:</td><td colspan=\"2\">$lynis_report_data{'network_ipv6_address[]'}</td></tr>\n";
}
} else {
print OUT "\t\t\t\t\t<tr><td class=\"field_label\" colspan=\"2\">ipv6 addresses:</td><td colspan=\"2\">&nbsp;</td></tr>\n";
}
print OUT "\t\t\t\t\t<tr><td class=\"field_label\" colspan=\"2\">Default Gateway</td><td colspan=\"2\">$lynis_report_data{'default_gateway[]'}</td></tr>\n";
print OUT "\t\t\t\t\t<tr>\n";
#print STDERR "Should be ARRAY: |".ref($lynis_report_data{'network_mac_address[]'})."|\n";
if (ref($lynis_report_data{'network_mac_address[]'}) eq "ARRAY") {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">MAC Address:</td><td>".join("<br />\n", @{$lynis_report_data{'network_mac_address[]'}})."</td>\n";
} elsif ((defined($lynis_report_data{'network_mac_address[]'})) and ($lynis_report_data{'network_mac_address[]'} ne "")) {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">MAC Address:</td><td>$lynis_report_data{'network_mac_address[]'}</td>\n";
} else {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">MAC Address:</td><td>&nbsp;</td>\n";
}
print OUT <<END;
<td class="field_label">Name Cache Used:</td><td>$to_bool{$lynis_report_data{'name_cache_used'}}</td>
</tr>
END
if (ref($lynis_report_data{'nameserver[]'}) eq 'ARRAY') {
print OUT "\t\t\t\t\t\t<tr><td class=\"field_label\" colspan=\"2\">name servers:</td><td colspan=\"2\">".join("<br />\n", @{$lynis_report_data{'nameserver[]'}})."</td></tr>\n";
} else {
print OUT "\t\t\t\t\t\t<tr><td class=\"field_label\" colspan=\"2\">name servers:</td><td colspan=\"2\">$lynis_report_data{'nameserver[]'}</td></tr>\n";
}
print OUT <<END;
<tr>
<td class="field_label" colspan="2">resolv.conf search domain:</td>
END
if (ref($lynis_report_data{'resolv_conf_search_domain[]'}) eq 'ARRAY') {
print OUT "\t\t\t\t\t\t<td colspan=\"2\">".join("<br />\n",@{$lynis_report_data{'resolv_conf_search_domain[]'}})."</td>\n";
} else {
print OUT "\t\t\t\t\t\t<td colspan=\"2\">$lynis_report_data{'resolv_conf_search_domain[]'}</td>\n";
}
print OUT <<END;
</tr>
</table>
<h4>Open Ports:</h4>
<table>
<tr><td>IP Address</td><td>Port</td><td>Protocol</td><td>Daemon/Process</td><td>???</td></tr>
END
if (exists($lynis_report_data{'network_listen_port[]'})) {
foreach my $obj ( sort @{$lynis_report_data{'network_listen_port[]'}} ) {
my ($ipp,$proto,$daemon,$dunno) = split(/\|/, $obj);
my ($ip,$port);
if (grep(/\:/, split(//, $ipp)) > 1) {
# must be an IPv6 address;
my @parts = split(/\:/, $ipp);
$port = pop(@parts); # gets the last element of the array. like $parts[-1];
$ip = join(":", @parts); # should only be the remaining parts, which should be the ipv6 addr
} else {
# must be IPv4
($ip,$port) = split(/\:/, $ipp);
}
print OUT "\t\t\t\t\t<tr><td>$ip</td><td>$port</td><td>$proto</td><td>$daemon</td><td>$dunno</td></tr>\n";
}
}
#######################################
### SECURITY INFO
#######################################
print OUT <<END;
</table>
</div>
<hr />
<h3><a id="security_info">security info:</a></h3>
<div class="content_subsection">
<table>
<tr>
END
$lynis_report_data{'firewall_installed'} = 0 if ((!defined($lynis_report_data{'firewall_installed'})) or ($lynis_report_data{'firewall_installed'} eq ''));
print OUT "\t\t\t\t\t<td class=\"field_label\">Host Firewall Installed:</td><td>$to_bool{$lynis_report_data{'firewall_installed'}}</td>\n";
$lynis_report_data{'firewall_software'} = "&nbsp;" if ((!defined($lynis_report_data{'firewall_software'})) or ($lynis_report_data{'firewall_software'} eq ''));
print OUT "\t\t\t\t\t<td class=\"field_label\">Firewall Software:</td><td>$lynis_report_data{'firewall_software'}</td>\n";
$lynis_report_data{'firewall_empty_ruleset'} = 0 if ((!defined($lynis_report_data{'firewall_empty_ruleset'})) or ($lynis_report_data{'firewall_empty_ruleset'} eq ''));
print OUT "\t\t\t\t\t<td class=\"field_label\">Firewall Empty Ruleset:</td><td>$to_bool{$lynis_report_data{'firewall_empty_ruleset'}}</td>\n";
$lynis_report_data{'firewall_active'} = 0 if ((!defined($lynis_report_data{'firewall_active'})) or ($lynis_report_data{'firewall_active'} eq ''));
print OUT "\t\t\t\t\t<td class=\"field_label\">Firewall Active:</td><td>$to_bool{$lynis_report_data{'firewall_active'}}</td>\n";
print OUT <<END;
</tr>
<tr>
END
print OUT "\t\t\t\t\t\t<td class=\"field_label\">Package Audit Tools Found:</td><td>$to_bool{$lynis_report_data{'package_audit_tool_found'}}</td>\n";
print OUT "\t\t\t\t\t\t<td class=\"field_label\">Package Audit Tool:</td><td>$lynis_report_data{'package_audit_tool'}</td>\n";
print OUT "\t\t\t\t\t\t<td class=\"field_label\">Vulnerable Packages Found:</td><td>$lynis_report_data{'vulnerable_packages_found'}</td>\n";
print OUT "\t\t\t\t\t\t<td class=\"field_label\">Package Manager:</td><td>$lynis_report_data{'package_manager[]'}</td>\n";
print OUT <<END;
</tr>
<tr>
END
$lynis_report_data{'authentication_two_factor_enabled'} = 0 if ((!defined($lynis_report_data{'authentication_two_factor_enabled'})) or ($lynis_report_data{'authentication_two_factor_enabled'} eq ''));
print OUT "\t\t\t\t\t\t<td class=\"field_label\">Two-Factor Authentication Enabled:</td><td>$to_bool{$lynis_report_data{'authentication_two_factor_enabled'}}</td>\n";
$lynis_report_data{'authentication_two_factor_required'} = 0 if ((!defined($lynis_report_data{'authentication_two_factor_required'})) or ($lynis_report_data{'authentication_two_factor_required'} eq ''));
print OUT "\t\t\t\t\t\t<td class=\"field_label\">Two-Factor Authentication Required:</td><td>$to_bool{$lynis_report_data{'authentication_two_factor_required'}}</td>\n";
print OUT "\t\t\t\t\t\t<td class=\"field_label\">LDAP PAM Module Enabled:</td><td>$to_bool{$lynis_report_data{'ldap_pam_enabled'}}</td>\n";
print OUT "\t\t\t\t\t\t<td class=\"field_label\">LDAP Auth Enabled:</td><td>$to_bool{$lynis_report_data{'ldap_auth_enabled'}}</td>\n";
print OUT <<END;
</tr>
<tr>
END
$lynis_report_data{'minimum_password_length'} = 0 if ((!defined($lynis_report_data{'minimum_password_length'})) or ($lynis_report_data{'minimum_password_length'} eq ''));
print OUT "\t\t\t\t\t\t<td class=\"field_label\">Minimum Password Length:</td><td>$lynis_report_data{'minimum_password_length'}</td>\n";
$lynis_report_data{'password_max_days'} = 0 if ((!defined($lynis_report_data{'password_max_days'})) or ($lynis_report_data{'password_max_days'} eq ''));
print OUT "\t\t\t\t\t\t<td class=\"field_label\">Maximum Password Days:</td><td>$lynis_report_data{'password_max_days'}</td>\n";
$lynis_report_data{'password_min_days'} = 0 if ((!defined($lynis_report_data{'password_min_days'})) or ($lynis_report_data{'password_min_days'} eq ''));
print OUT "\t\t\t\t\t\t<td class=\"field_label\">Minimum Password Days:</td><td>$lynis_report_data{'password_min_days'}</td>\n";
$lynis_report_data{'max_password_retry'} = 0 if ((!defined($lynis_report_data{'max_password_retry'})) or ($lynis_report_data{'max_password_retry'} eq ''));
print OUT "\t\t\t\t\t\t<td class=\"field_label\">Maximum Password Retries:</td><td>$lynis_report_data{'max_password_retry'}</td>\n";
print OUT <<END;
</tr>
<tr>
END
printf OUT "\t\t\t\t\t\t<td class=\"field_label\">Password Complexity Score:</td><td>%#b</td>\n", $pass_score;
print OUT "\t\t\t\t\t\t<td class=\"field_label\">PAM Cracklib Found:</td><td>$to_bool{$lynis_report_data{'pam_cracklib'}}</td>\n";
$lynis_report_data{'password_strength_tested'} = 0 if ((!defined($lynis_report_data{'password_strength_tested'})) or ($lynis_report_data{'password_strength_tested'} eq ''));
print OUT "\t\t\t\t\t\t<td class=\"field_label\">Password Strength Tested:</td><td>$to_bool{$lynis_report_data{'password_strength_tested'}}</td>\n";
print OUT "\t\t\t\t\t\t<td class=\"field_label\">PAM Password Quality:</td><td>$lynis_report_data{'pam_pwquality'}</td>\n";
print OUT <<END;
</tr>
<tr>
END
if (exists($lynis_report_data{'file_integrity_tool_installed'})) {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">File Integrity Tool Installed:</td><td>$to_bool{$lynis_report_data{'file_integrity_tool_installed'}}</td>\n";
} else {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">File Integrity Tools Installed:</td><td>false</td>\n";
}
if (exists($lynis_report_data{'file_integrity_tool'})) {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">File Integrity Tool:</td><td>$lynis_report_data{'file_integrity_tool'}</td>\n";
} else {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">File Integrity Tool:</td><td>NA</td>\n";
}
print OUT "\t\t\t\t\t\t<td class=\"field_label\">Automation Tool Present:</td><td>$to_bool{$lynis_report_data{'automation_tool_present'}}</td>\n";
if (ref($lynis_report_data{'automation_tool_running[]'}) eq 'ARRAY') {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">Automation Tool:</td><td>".join("<br />\n", @{$lynis_report_data{'automation_tool_running[]'}})."</td>\n";
} elsif ((defined($lynis_report_data{'automation_tool_running[]'})) and ($lynis_report_data{'automation_tool_running[]'} ne "")) {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">Automation Tool:</td><td>$lynis_report_data{'automation_tool_running[]'}</td>\n";
} else {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">Automation Tool:</td><td>&nbsp;</td>\n";
}
print OUT <<END;
</tr>
<tr>
<td class="field_label">Malware Scanner Installed:</td><td>$to_bool{$lynis_report_data{'malware_scanner_installed'}}</td>
END
if (ref($lynis_report_data{'malware_scanner[]'}) eq 'ARRAY') {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">Malware Scanner(s):</td><td>".join("<br />\n", @{$lynis_report_data{'malware_scanner[]'}})."</td>\n";
} else {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">Malware Scanner(s):</td><td>$lynis_report_data{'malware_scanner[]'}</td>\n";
}
print OUT <<END;
<td class="field_label">compiler installed:</td><td>$to_bool{$lynis_report_data{'compiler_installed'}}</td>
END
if (ref($lynis_report_data{'compiler[]'}) eq 'ARRAY') {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">compilers:</td><td>".join("<br />\n", @{$lynis_report_data{'compiler[]'}})."</td>\n";
} else {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">compilers:</td><td>$lynis_report_data{'compiler[]'}</td>\n";
}
print OUT <<END;
</tr>
<tr>
END
if (ref($lynis_report_data{'ids_ips_tooling[]'}) eq 'ARRAY') {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">IDS/IPS Tooling</td><td>".join("<br />\n", @{$lynis_report_data{'ids_ips_tooling[]'}})."</td>\n";
} else {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">IDS/IPS Tooling</td><td>$lynis_report_data{'ids_ips_tooling[]'}</td>\n";
}
print OUT "\t\t\t\t\t\t<td class=\"field_label\">Failed Logins Logged:</td><td>$lynis_report_data{'auth_failed_logins_logged'}</td>\n";
if (ref($lynis_report_data{'fail2ban_config'}) eq 'ARRAY') {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">fail2ban config file(s):</td><td>".join("<br />\n", @{$lynis_report_data{'fail2ban_config'}})."</td>\n";
} else {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">fail2ban config file(s):</td><td>$lynis_report_data{'fail2ban_config'}</td>\n";
}
if (ref($lynis_report_data{'fail2ban_enabled_service[]'}) eq 'ARRAY') {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">fail2ban enabled service(s):</td><td>".join("<br />\n", @{$lynis_report_data{'fail2ban_enabled_service[]'}})."</td>\n";
} else {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">fail2ban enabled service(s):</td><td>$lynis_report_data{'fail2ban_enabled_service[]'}</td>\n";
}
print OUT "</tr>\n";
print OUT "<tr><td class=\"field_label\">AppArmor Enabled:</td><td>$to_bool{$lynis_report_data{'apparmor_enabled'}}</td>\n";
print OUT "<td class=\"field_label\">AppArmor Policy Loaded:</td><td>$to_bool{$lynis_report_data{'apparmor_policy_loaded'}}</td>\n";
print OUT "<td class=\"field_label\">SELinux Status:</td><td>$to_bool{$lynis_report_data{'selinux_status'}}</td>\n";
print OUT "<td class=\"field_label\">SELinux mode:</td><td>$lynis_report_data{'selinux_mode'}</td></tr>\n";
print OUT "<tr><td class=\"field_label\">Group Names Unique</td><td>$to_bool{$lynis_report_data{'auth_group_names_unique'}}</td>\n";
print OUT "<td class=\"field_label\">Group IDs Unique</td><td>$to_bool{$lynis_report_data{'auth_group_ids_unique'}}</td>\n";
print OUT "<td class=\"field_label\"></td><td></td>\n";
print OUT "<td class=\"field_label\"></td><td></td></tr>\n";
print OUT <<END;
</table>
<table class="list">
<tr><td><h4>real users:</h4></td><td><h4>home directories:</h4></td></tr>
<tr><td>
<table class="list">
<tr><td>name</td><td>uid</td></tr>
END
#print "Hash element data type: ".ref($lynis_report_data{'real_user[]'})."\n";
if ((defined($lynis_report_data{'real_user[]'})) and ($lynis_report_data{'real_user[]'} ne "")) {
if (ref($lynis_report_data{'real_user[]'}) eq 'ARRAY') {
foreach my $u ( @{$lynis_report_data{'real_user[]'}} ) {
my ($name,$uid) = split(/,/, $u);
print OUT "\t\t\t\t\t\t\t<tr><td>$name</td><td>$uid</td></tr>\n";
}
} else {
my ($name,$uid) = split(/,/, $lynis_report_data{'real_user[]'});
print OUT "\t\t\t\t\t\t\t<tr><td>$name</td><td>$uid</td></tr>\n";
}
} else {
print OUT "\t\t\t\t\t\t\t<tr><td>NA</td><td>NA</td></tr>\n";
}
print OUT "\t\t\t\t\t\t</table></td><td><select size=\"10\" name=\"lbHomeDirectories\">\n";
foreach my $d ( @{$lynis_report_data{'home_directory[]'}} ) { print OUT "\t\t\t\t\t\t\t<option>$d\n"; }
print OUT <<END;
</select></td></tr>
</table>
<h4>PAM Modules:</h4><a id="pamModLink" href="javascript:toggle('pamModLink', 'pamModToggle');">&gt;&nbsp;show&nbsp;&lt;</a>
<div id="pamModToggle" style="display: none">
END
if (ref($lynis_report_data{'pam_module[]'}) eq 'ARRAY') {
if (scalar(@{$lynis_report_data{'pam_module[]'}}) < 25) {
print OUT "<select id=\"pam_module[]\" size=\"".scalar(@{$lynis_report_data{'pam_module[]'}})."\">\n";
} else {
print OUT "<select id=\"pam_module[]\" size=\"25\">\n";
}
foreach my $pm ( sort @{$lynis_report_data{'pam_module[]'}} ) {
print OUT "\t\t\t\t\t\t<option>$pm\n";
}
print OUT "</select>\n";
}
if ((!defined($lynis_report_data{'boot_service_tool'})) or ($lynis_report_data{'boot_service_tool'} eq "")) { $lynis_report_data{'boot_service_tool'} = "&nbsp;"; }
####################################
### BOOT INFO
####################################
print OUT <<END;
</table>
</div>
</div>
<hr />
<h3><a id="boot_info">boot info:</a></h3>
<div class="content_subsection">
<table>
<tr>
<td class="field_label">UEFI booted:</td><td>$to_bool{$lynis_report_data{'boot_uefi_booted'}}</td>
<td class="field_label">UEFI booted secure:</td><td>$to_bool{$lynis_report_data{'boot_uefi_booted_secure'}}</td>
</tr>
<tr>
<td class="field_label">default runlevel:</td><td>$lynis_report_data{'linux_default_runlevel'}</td>
<td class="field_label">boot service tool:</td><td>$lynis_report_data{'boot_service_tool'}</td>
</tr>
</table>
END
print OUT "\t\t\t\t<h4>services started at boot:</h4>\n";
if (!defined($lynis_report_data{'boot_service[]'})) {
print OUT "\t\t\t\t\t<ul><li>N/A - Unable to detect boot services.</li></ul>\n";
} elsif (ref($lynis_report_data{'boot_service[]'}) eq "ARRAY") {
if (scalar(@{$lynis_report_data{'boot_service[]'}}) < 10) {
print OUT "\t\t\t\t\t\t<select size=\"".scalar(@{$lynis_report_data{'boot_service[]'}})."\">\n";
} else {
print OUT "\t\t\t\t\t\t<select size=\"10\">\n";
}
foreach my $svc ( @{$lynis_report_data{'boot_service[]'}} ) {
print OUT "\t\t\t\t\t\t<option>$svc\n";
}
print OUT "\t\t\t\t\t</select>\n";
} else {
if ((($verbose) and ($verbose > 1)) or ($debug)) {
warn colored("boot_service[] object not an array", "yellow");
print Dumper($lynis_report_data{'boot_service[]'});
}
}
$lynis_report_data{'linux_kernel_io_scheduler'} = "&nbsp;" if ((!defined($lynis_report_data{'linux_kernel_io_scheduler'})) or ($lynis_report_data{'linux_kernel_io_scheduler'} eq ""));
$lynis_report_data{'linux_amount_of_kernels'} = "&nbsp;" if ((!defined($lynis_report_data{'linux_amount_of_kernels'})) or ($lynis_report_data{'linux_amount_of_kernels'} eq ""));
#print Dumper($lynis_report_data{'linux_kernel_io_scheduler'});
##########################################
### KERNEL INFO
##########################################
print OUT <<END;
</div>
<hr />
<h3><a id="kernel_info">kernel info:</a></h3>
<div class="content_subsection">
<table>
<tr>
END
print OUT "\t\t\t\t\t<td class=\"field_label\">kernel version:</td><td>$lynis_report_data{'linux_kernel_version'}</td>\n";
print OUT "\t\t\t\t\t<td class=\"field_label\">full kernel version:</td><td>$lynis_report_data{'os_kernel_version_full'}</td>\n";
print OUT <<END;
</tr>
<tr>
END
print OUT "\t\t\t\t\t<td class=\"field_label\">kernel release version:</td><td>$lynis_report_data{'linux_kernel_release'}</td>\n";
if (ref($lynis_report_data{'linux_kernel_io_scheduler[]'}) eq 'ARRAY') {
print OUT "\t\t\t\t\t<td class=\"field_label\">kernel IO scheduler:</td><td>".join("<br />\n", @{$lynis_report_data{'linux_kernel_io_scheduler[]'}})."</td>\n";
} else {
print OUT "\t\t\t\t\t<td class=\"field_label\">kernel IO scheduler:</td><td>$lynis_report_data{'linux_kernel_io_scheduler[]'}</td>\n";
}
print OUT <<END;
</tr>
<tr>
END
print OUT "\t\t\t\t\t<td class=\"field_label\">linux kernel type:</td><td>$lynis_report_data{'linux_kernel_type'}</td>\n";
print OUT "\t\t\t\t\t<td class=\"field_label\">number of kernels available:</td><td>$lynis_report_data{'linux_amount_of_kernels'}</td>\n";
print OUT <<END;
</tr>
</table>
<h4>kernel modules loaded:</h4><a id="kernelModLink" href="javascript:toggle('kernelModLink', 'kernelModToggle');">&gt;&nbsp;show&nbsp;&lt;</a>
<div id="kernelModToggle" style="display: none">
END
if (ref($lynis_report_data{'loaded_kernel_module[]'}) eq 'ARRAY') {
if (scalar(@{$lynis_report_data{'loaded_kernel_module[]'}}) < 25) {
print OUT "\t\t\t\t\t\t<select size=\"".scalar(@{$lynis_report_data{'loaded_kernel_module[]'}})."\">\n";
} else {
print OUT "\t\t\t\t\t\t<select size=\"25\">\n";
}
foreach my $m ( sort @{$lynis_report_data{'loaded_kernel_module[]'}} ) { print OUT "\t\t\t\t\t\t\t<option>$m\n"; }
print OUT "\t\t\t\t\t\t</select>\n";
}
$lynis_report_data{'journal_oldest_bootdate'} = "&nbsp;" if ((!defined($lynis_report_data{'journal_oldest_bootdate'})) or ($lynis_report_data{'journal_oldest_bootdate'} eq ""));
$lynis_report_data{'journal_contains_errors'} = 0 if ((!defined($lynis_report_data{'journal_contains_errors'})) or ($lynis_report_data{'journal_contains_errors'} eq ""));
print OUT <<END;
</div>
</div>
<hr />
<h3><a id="filesystem_info">filesystem/journalling info:</a></h3>
<div class="content_subsection">
<table>
<tr>
END
print OUT "\t\t\t\t\t<td class=\"field_label\">oldest boot date:</td><td>$lynis_report_data{'journal_oldest_bootdate'}</td>\n";
print OUT "\t\t\t\t\t<td class=\"field_label\">journal errors:</td><td>$to_bool{$lynis_report_data{'journal_contains_errors'}}</td>\n";
print OUT <<END;
</tr>
<tr>
END
print OUT "\t\t\t\t\t<td class=\"field_label\">journal disk size:</td><td>$lynis_report_data{'journal_disk_size'}</td>\n";
print OUT "\t\t\t\t\t<td class=\"field_label\">last cordumps:</td><td>$lynis_report_data{'journal_coredumps_lastday'}</td>\n";
print OUT <<END;
</tr>
<tr>
END
if ((exists($lynis_report_data{'file_systems_ext[]'})) and (ref($lynis_report_data{'file_systems_ext[]'}) eq "ARRAY")) {
print OUT "\t\t\t\t\t\t<td>filesystems:</td><td>".join("<br />\n", @{$lynis_report_data{'file_systems_ext[]'}})."</td>\n";
} else {
if (defined($lynis_report_data{'file_systems_ext[]'})) {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">filesystems:</td><td>$lynis_report_data{'file_systems_ext[]'}</td>\n";
} else {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">filesystems:</td><td>&nbsp;</td>\n";
}
}
if ((exists($lynis_report_data{'swap_partition[]'})) and (ref($lynis_report_data{'swap_partition[]'}) eq "ARRAY")) {
#warn colored("swap_partition[] is an array".Dumper(\@{$lynis_report_data{'swap_partition[]'}}), "yellow") if ($verbose);
warn colored("swap_partition[] is an array", "yellow") if ((($verbose) and ($verbose > 1 )) or ($debug));
if (scalar(@{$lynis_report_data{'swap_partition[]'}}) == 1) {
if ($lynis_report_data{'swap_partition[]'}[0] =~ /\,/) {
my @p = split(/\,/, $lynis_report_data{'swap_partition[]'}[0]);
$lynis_report_data{'swap_partition[]'} = \@p;
}
} else {
#if (scalar(@{$lynis_report_data{'swap_partition[]'}}) > 1) {
# print color('bold magenta');
# print Dumper($lynis_report_data{'swap_partition[]'});
# print color('reset');
# @{$lynis_report_data{'swap_partition[]'}} = &dedup_array(@{$lynis_report_data{'swap_partition[]'}});
#}
}
print OUT "\t\t\t\t\t\t<td class=\"field_label\">swap partitions:</td><td>".join("<br />\n", @{$lynis_report_data{'swap_partition[]'}})."</td>\n";
} else {
if ((($verbose) and ($verbose > 1)) or ($debug)) {
warn colored("swap_partition[] is a string.", "yellow") if ($verbose);
print OUT "\t\t\t\t\t\t<td class=\"field_label\">swap partitions:</td><td>$lynis_report_data{'swap_partition[]'}</td>\n";
}
}
$lynis_report_data{'journal_bootlogs'} = 0 if ((!defined($lynis_report_data{'journal_bootlogs'})) or ($lynis_report_data{'journal_bootlogs'} eq ""));
print OUT <<END;
</tr>
END
if (ref($lynis_report_data{'lvm_volume_group[]'}) eq 'ARRAY') {
print OUT "\t\t\t\t\t\t<tr><td class=\"field_label\">LVM volume group(s):</td><td>".join("<br />\n", @{$lynis_report_data{'lvm_volume_group[]'}})."</td>";
} else {
print OUT "\t\t\t\t\t\t<tr><td class=\"field_label\">LVM volume group(s):</td><td>$lynis_report_data{'lvm_volume_group[]'}</td>";
}
if (ref($lynis_report_data{'lvm_volume[]'}) eq 'ARRAY') {
print OUT "<td class=\"field_label\">LVM volume(s)</td><td>".join("<br />\n", @{$lynis_report_data{'lvm_volume[]'}})."</td></tr>\n";
} else {
print OUT "<td class=\"field_label\">LVM volume(s)</td><td>$lynis_report_data{'lvm_volume[]'}</td></tr>\n";
}
print OUT <<END;
<tr>
<td class="field_label">journal boot log found:</td><td>$to_bool{$lynis_report_data{'journal_bootlogs'}}</td>
<td class="field_label"></td><td></td>
</tr>
</table>
<br />
<h4>journal metadata:</h4><a id="journalMetaDataLink" href="javascript:toggle('journalMetaDataLink', 'journalMetaDataToggle');">&gt;&nbsp;show&nbsp;&lt;</a>
<div id="journalMetaDataToggle" style="display:none">
END
if ((exists($lynis_report_data{'journal_meta_data'})) and (ref($lynis_report_data{'journal_meta_data'}) eq "ARRAY")) {
foreach my $md ( @{$lynis_report_data{'journal_meta_data'}} ) {
print OUT "\t\t\t\t\t<table>\n";
my @fields = split(/,/, $md);
foreach my $f ( @fields ) {
my ($key,$val);
#print grep(/\:/, split(//, $f))."\n";
if (grep(/\:/, split(//, $f)) > 1) {
$key = substr($f,0,index($f,":"));
$val = substr($f,(index($f,":")+1));
} else {
($key,$val) = split(/:/, $f);
}
#print "k: $key v: $val \n";
next if (!defined($key));
if ((!defined($val)) or ($val eq "")) { $val = "&nbsp;"; }
print OUT "\t\t\t\t\t\t<tr><td>$key\:</td><td>$val</td></tr>\n";
}
print OUT "\t\t\t\t\t</table>\n<br />\n";
}
} else {
warn colored("Didn't find journal_meta_data object!", "yellow") if ((($verbose) and ($verbose > 1)) or ($debug));
}
##########################################
### SERVICE INFO
##########################################
print OUT <<END;
</div>
</div>
<hr />
<h3><a id="service_info">service info:</a></h3>
<div class="content_subsection">
<table>
END
foreach my $prog ( sort qw( redis ntp_daemon mysql ssh_daemon dhcp_client arpwatch audit_daemon postgresql linux_auditd nginx ) ) {
if ((defined($lynis_report_data{$prog.'_running'})) and ($lynis_report_data{$prog.'_running'} ne "")) {
print OUT "\t\t\t\t\t<tr><td class=\"field_label\">$prog running:</td><td>$to_bool{$lynis_report_data{$prog.'_running'}}</td></tr>\n";
} else {
print OUT "\t\t\t\t\t<tr><td class=\"field_label\">$prog running:</td><td>$to_bool{0}</td></tr>\n";
}
}
print OUT "\t\t\t\t</table>\n";
print OUT "\t\t\t\t<h4>daemon info:</h4>\n";
print OUT "\t\t\t\t\t<table>\n";
print OUT "\t\t\t\t\t\t<tr><td class=\"field_label\">pop3 daemon:</td><td>$lynis_report_data{'pop3_daemon'}</td></tr>\n";
print OUT "\t\t\t\t\t\t<tr><td class=\"field_label\">imap daemon:</td><td>$lynis_report_data{'imap_daemon'}</td></tr>\n";
print OUT "\t\t\t\t\t\t<tr><td class=\"field_label\">smtp daemon:</td><td>$lynis_report_data{'smtp_daemon'}</td></tr>\n";
print OUT "\t\t\t\t\t\t<tr><td class=\"field_label\">printing daemon:</td><td>$lynis_report_data{'printing_daemon'}</td></tr>\n";
print OUT "\t\t\t\t\t\t<tr><td class=\"field_label\">ntp daemon:</td><td>$lynis_report_data{'ntp_daemon'}</td></tr>\n";
if (ref($lynis_report_data{'scheduler[]'}) eq 'ARRAY') {
print OUT "\t\t\t\t\t\t<tr><td class=\"field_label\">scheduler(s):</td><td>".join("<br />\n",@{$lynis_report_data{'scheduler[]'}})."</td></tr>\n";
} else {
print OUT "\t\t\t\t\t\t<tr><td class=\"field_label\">scheduler(s):</td><td>$lynis_report_data{'scheduler[]'}</td></tr>\n";
}
print OUT "\t\t\t\t\t\t<tr><td class=\"field_label\">service manager:</td><td>$lynis_report_data{'service_manager'}</td></tr>\n";
print OUT "\t\t\t\t\t\t<tr><td class=\"field_label\">running service tool:</td><td>$lynis_report_data{'running_service_tool'}</td></tr>\n";
print OUT "\t\t\t\t\t</table>\n";
print OUT <<END;
<h4>running services:</h4>
END
if (scalar(@{$lynis_report_data{'running_service[]'}}) < 25) {
print OUT "\t\t\t\t<select size=\"".scalar(@{$lynis_report_data{'running_service[]'}})."\">\n";
} else {
print OUT "\t\t\t\t<select size=\"25\">\n";
}
foreach my $svc ( @{$lynis_report_data{'running_service[]'}} ) { print OUT "\t\t\t\t\t<option>$svc\n"; }
print OUT "\t\t\t\t</select>\n";
print OUT <<END;
<h5>ntp detail:</h5><a id="ntpDetailLink" href="javascript: toggle('ntpDetailLink','ntpDetailToggle');">&gt;&nbsp;show&nbsp;&lt;</a>
<div id="ntpDetailToggle" style="display: none">
<table>
<tr>
<td>ntp config found:</td><td>$to_bool{$lynis_report_data{'ntp_config_found'}}</td>
END
if (ref($lynis_report_data{'ntp_config_file[]'}) eq 'ARRAY') {
print OUT "\t\t\t\t\t\t\t<td>ntp config file:</td><td>".join("<br />\n", @{$lynis_report_data{'ntp_config_file[]'}})."</td>\n";
} else {
#warn colored("ntp config file object not an array! \n", "yellow");
print OUT "\t\t\t\t\t\t\t<td>ntp config file:</td><td>$lynis_report_data{'ntp_config_file[]'}</td>\n";
}
print OUT <<END;
</tr>
<tr>
END
print OUT "\t\t\t\t\t\t\t<td>ntp version:</td><td>$lynis_report_data{'ntp_version'}</td>\n";
if (ref($lynis_report_data{'ntp_unreliable_peer[]'}) eq 'ARRAY') {
print OUT "\t\t\t\t\t\t\t<td>unreliable peers:</td><td>".join("<br />\n", @{$lynis_report_data{'ntp_unreliable_peer[]'}})."</td>";
} else {
print OUT "\t\t\t\t\t\t\t<td>unreliable peers:</td><td>$lynis_report_data{'ntp_unreliable_peer[]'}</td>\n";
}
print OUT <<END;
</tr>
<tr><th colspan="4">NTP Config Type</th></tr>
<tr>
END
print OUT "\t\t\t\t\t\t\t<td>startup:</td><td>$to_bool{$lynis_report_data{'ntp_config_type_startup'}}</td>\n";
print OUT "\t\t\t\t\t\t\t<td>daemon:</td><td>$to_bool{$lynis_report_data{'ntp_config_type_daemon'}}</td>\n";
print OUT <<END;
</tr>
<tr>
END
print OUT "\t\t\t\t\t\t\t<td>scheduled:</td><td>$to_bool{$lynis_report_data{'ntp_config_type_scheduled'}}</td>\n";
print OUT "\t\t\t\t\t\t\t<td>event based:</td><td>$to_bool{$lynis_report_data{'ntp_config_type_eventbased'}}</td>\n";
print OUT <<END;
</tr>
</table>
</div><!-- END ntpDetailToggle div -->
<br />
<h5>nginx detail</h5>
<table>
<tr>
END
print OUT "\t\t\t\t\t\t<td class=\"field_label\">main config file:</td><td>$lynis_report_data{'nginx_main_conf_file'}</td>\n";
if (ref($lynis_report_data{'nginx_sub_conf_file[]'}) eq 'ARRAY') {
print OUT "\t\t\t\t\t<td class=\"field_label\">other config file(s):</td><td>".join("<br />\n", @{$lynis_report_data{'nginx_sub_conf_file[]'}})."</td>\n";
} else {
print OUT "\t\t\t\t\t<td class=\"field_label\">other config file(s):</td><td>$lynis_report_data{'nginx_sub_conf_file[]'}</td>\n";
}
print OUT <<END;
</tr>
<tr>
END
if (ref($lynis_report_data{'log_file'}) eq 'ARRAY') {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">log file:</td><td>".join("<br />\n",@{$lynis_report_data{'log_file'}})."</td>\n";
} else {
print OUT "\t\t\t\t\t\t<td class=\"field_label\">log file:</td><td>$lynis_report_data{'log_file'}</td>\n";
}
print OUT <<END;
<td class="field_label"></td><td></td>
</tr>
</table>
END
print OUT "\t\t\t\t<h5>nginx config options:</h5><a id=\"nginxConfigLink\" href=\"javascript: toggle('nginxConfigLink', 'nginxConfigToggle');\">&gt;&nbsp;show&nbsp;&lt;</a>\n";
print OUT "\t\t\t\t\t<div id=\"nginxConfigToggle\" style=\"display:none;\">\n";
print OUT "\t\t\t\t\t<ul>\n";
if (ref($lynis_report_data{'nginx_config_option[]'}) eq 'ARRAY') {
foreach my $o ( @{$lynis_report_data{'nginx_config_option[]'}} ) { print OUT "\t\t\t\t\t\t<li>$o</li>\n"; }
} else {
if ((defined($lynis_report_data{'nginx_config_option[]'})) and ($lynis_report_data{'nginx_config_option[]'} ne "")) {
print OUT "\t\t\t\t\t\t<li>$lynis_report_data{'nginx_config_option[]'}</li>\n";
} else {
print OUT "\t\t\t\t\t\t<li>N/A - Unable to detect nginx config </li>\n";
warn colored("nginx config options opbject not an array!", "yellow");
print Dumper($lynis_report_data{'nginx_config_option[]'});
}
}
print OUT "\t\t\t\t\t</ul>\n";
print OUT <<END;
</div><!-- END nginxConfigToggle div --><br />
<h5>SSL/TLS protocols enabled:</h5>
<a id="ssltlsProtoLink" href="javascript: toggle('ssltlsProtoLink', 'ssltlsProtoToggle');">&gt;&nbsp;show&nbsp;&lt;</a>
<div id="ssltlsProtoToggle" style="display:none;">
<ul>
END
if (ref($lynis_report_data{'ssl_tls_protocol_enabled[]'}) eq 'ARRAY') {
foreach my $p ( @{$lynis_report_data{'ssl_tls_protocol_enabled[]'}} ) { print OUT "\t\t\t\t\t\t<li>$p</li>\n"; }
} else {
print OUT "\t\t\t\t\t\t<li>$lynis_report_data{'ssl_tls_protocol_enabled[]'}</li>\n";
#warn colored("ssltls protocols object not an array! \n", "yellow");
#print Dumper($lynis_report_data{'ssl_tls_protocol_enabled[]'});
}
print OUT "\t\t\t\t\t</ul>\n";
print OUT "\t\t\t\t</div><!-- END ssltlsProtoToggle div --><br />\n";
if (ref($lynis_report_data{'apache_version'}) eq 'ARRAY') {
die colored("apache version is an array:\n".Dumper(\@{$lynis_report_data{'apache_version'}}), "bold red");
}
print OUT <<END;
<h5>apache details:</h5>
<a id="apacheDetailsLink" href="javascript:toggle('apacheDetailsLink','apacheDetailsToggle');">&gt; &nbsp; show &nbsp; &lt;</a>
<div id="apacheDetailsToggle" style="display:none;">
<table><tr><td>apache version:</td><td>$lynis_report_data{'apache_version'}</td></tr></table>
<h5>apache modules found:</h5>
<a id="apacheModulesLink" href="javascript:toggle('apacheModulesLink','apacheModulesToggle');">&gt; &nbsp; show &nbsp; &lt;</a>
<div id="apacheModulesToggle" style="display:none;">
<ul>
END
if (ref($lynis_report_data{'apache_module[]'}) eq 'ARRAY') {
foreach my $m ( sort @{$lynis_report_data{'apache_module[]'}} ) { print OUT "\t\t\t\t\t\t\t\t<li>$m</li>\n"; }
} else {
if ((($verbose) and ($verbose > 1)) or ($debug)) {
warn colored("apache module object not an array!", "yellow");
print Dumper($lynis_report_data{'apache_module[]'});
}
}
print OUT "\t\t\t\t\t\t\t</ul>\n";
print OUT "\t\t\t\t\t\t</div><!-- END apacheModulesToggle div -->\n";
print OUT "\t\t\t\t\t</div><!-- END apacheDetailsToggle div -->\n";
print OUT <<END;
<h5>systemd detail:</h5><a id="systemdLink" href="javascript:toggle('systemdLink', 'systemdToggle');">&gt;&nbsp;show&nbsp;&lt;</a>
<div id="systemdToggle" style="display:none;">
<table>
<tr>
END
print OUT "\t\t\t\t\t\t<td>systemd version:</td><td>$lynis_report_data{'systemd_version'}</td>\n";
print OUT "\t\t\t\t\t\t<td>systemd status:</td><td>$lynis_report_data{'systemd_status'}</td>\n";
print OUT <<END;
</tr>
<tr>
END
print OUT "\t\t\t\t\t\t<td>systemd builtin components:</td><td colspan=\"3\">$lynis_report_data{'systemd_builtin_components'}</td>\n";
print OUT <<END;
</tr>
</table>
END
print OUT <<END;
<h5>systemd unit files:</h5><a id="systemdUnitFileLink" href="javascript:toggle('systemdUnitFileLink','systemdUnitFileToggle');">&gt; &nbsp; show &nbsp; &lt;</a>
<div id="systemdUnitFileToggle" style="display:none;">
<table>
<tr><th>unit file</th><th>status</th><th></th><tr>
END
if (ref($lynis_report_data{'systemd_unit_file[]'}) eq 'ARRAY') {
foreach my $f ( sort @{$lynis_report_data{'systemd_unit_file[]'}} ) {
my ($f,$s,$t) = split(/\|/, $f);
print OUT "\t\t\t\t\t\t\t<tr><td>$f</td><td>$s</td><td>$t</td></tr>\n";
}
} else {
warn colored("systemd unit file object not an array! ", "yellow") if ((($verbose) and ($verbose > 1)) or ($debug));
}
print OUT <<END;
</table>
</div><!-- END systemdUnitFileToggle div -->
END
print OUT <<END;
<h5>systemd unit not found:</h5><a id="systemdUnitNotFoundLink" href="javascript:toggle('systemdUnitNotFoundLink','systemdUnitNotFoundToggle');">&gt; &nbsp; show &nbsp; &lt;</a>
<div id="systemdUnitNotFoundToggle" style="display:none;">
<ul>
END
if (ref($lynis_report_data{'systemd_unit_not_found[]'})) {
foreach my $unf ( sort @{$lynis_report_data{'systemd_unit_not_found[]'}} ) { print OUT "\t\t\t\t\t\t\t<li>$unf</li>\n"; }
} else {
warn colored("systemd unitnot found object not an array! ", "yellow") if ((($verbose) and ($verbose > 1)) or ($debug));
}
print OUT <<END;
</ul>
</div><!-- END systemdUnitNotFoundToggle div -->
END
print OUT <<END;
<h5>systemd service not found:</h5><a id="systemdServiceNotFoundLink" href="javascript:toggle('systemdServiceNotFoundLink','systemdServiceNotFoundToggle');">&gt; &nbsp; show &nbsp; &lt;</a>
<div id="systemdServiceNotFoundToggle" style="display:none;">
<ul>
END
if (ref($lynis_report_data{'systemd_service_not_found[]'}) eq 'ARRAY') {
foreach my $snf ( sort @{$lynis_report_data{'systemd_service_not_found[]'}} ) { print OUT "\t\t\t\t\t\t\t<li>$snf</li>\n"; }
} else {
warn colored("systemd service not found object not an array! ", "yellow") if ((($verbose) and ($verbose > 1)) or ($debug));
}
print OUT <<END;
</ul>
</div><!-- END systemdServiceNotFoundToggle div -->
</div><!-- END systemdToggle -->
</div><!-- END subcontent div -->
<hr />
<h3><a id="installed_packages">Installed packages:</a></h3>
<div class="content_subsection">
<table>
<tr>
<td class="field_label">Number of packages installed:</td><td>$lynis_report_data{'installed_packages'}</td>
<td class="field_label">Number of binaries found:</td><td>$lynis_report_data{'binaries_count'}</td>
</tr>
</table>
<br />
<a id="pkgLink" href="javascript: toggle('pkgLink', 'pkgContent');">&gt;&nbsp;show&nbsp;&lt;</a>
<div id="pkgContent" style="display: none">
END
if (ref($lynis_report_data{'installed_packages_array'}) eq 'ARRAY') {
if (scalar(@{$lynis_report_data{'installed_packages_array'}}) < 25) {
print OUT "\t\t\t\t\t<select size=\"".scalar(@{$lynis_report_data{'installed_packages_array'}})."\">\n";
} else {
print OUT "\t\t\t\t\t<select size=\"25\">\n";
}
foreach my $p ( sort @{$lynis_report_data{'installed_packages_array'}} ) { chomp($p); print OUT "\t\t\t\t\t\t<option>$p\n"; }
print OUT "\t\t\t\t\t</select>\n";
}
print OUT <<END;
</div> <!-- #jsToggle -->
</div> <!-- #subcontainer -->
<div id="footer">
<hr />
<p><a href="http://jigsaw.w3.org/css-validator/check/referer">
<img style="border:0;width:88px;height:31px;"
src="http://jigsaw.w3.org/css-validator/images/vcss"
alt="Valid CSS!" />
</a></p>
</div> <!-- #footer -->
</div> <!-- #container -->
</body>
</html>
END
close OUT or die colored("There was a problem closing the output file ($output): $! ", "bold red");
my @indexes = qw( lynis_version lynis_tests_done lynis_update_available license_key report_datetime_start report_datetime_end plugins_directory plugins_enabled finish report_version_major report_version_minor hostid hostid2 plugin_enabled_phase1[] hardening_index warning[] hostname domainname linux_kernel_version linux_config_file memory_size nameserver[] network_interface[] framework_grsecurity vm vmtype uptime_in_seconds linux_kernel_release os framework_selinux uptime_in_days os_fullname default_gateway[] cpu_nx cpu_pae linux_version os_version network_ipv6_address[] boot_loader suggestion[] manual manual[] linux_version cpu_pae cpu_nx network_ipv4_address[] network_mac_address[] os_name os_kernel_version os_kernel_version_full firewall_installed max_password_retry password_max_days password_min_days pam_cracklib password_strength_tested minimum_password_length package_audit_tool package_audit_tool_found );
my @idx2 = qw( vulnerable_packages_found firewall_active firewall_software[] firewall_software auth_failed_logins_logged authentication_two_factor_enabled memory_units default_gateway authentication_two_factor_required malware_scanner_installed file_integrity_tool_installed file_integrity_tool_installed pam_module[] ids_ips_tooling[] ipv6_mode ipv6_only name_cache_used ldap_pam_enabled ntp_daemon_running mysql_running ssh_daemon_running dhcp_client_running arpwatch_running running_service[] audit_daemon_running installed_packages binaries_count installed_packages_array crond_running network_listen_port[] firewall_empty_ruleset automation_tool_present automation_tool_running[] file_integrity_tool ldap_auth_enabled password_max_l_credit password_max_u_credit password_max_digital_credit password_max_other_credit loaded_kernel_module[] plugin_directory package_manager[] linux_kernel_io_scheduler[] linux_kernel_type );
my @idx3 = qw( details[] available_shell[] locate_db smtp_daemon smtp_daemon[] pop3_daemon ntp_daemon imap_daemon printing_daemon boot_service[] boot_uefi_boot_secure linux_default_runlevel boot_service_tool boot_uefi_booted systemctl_exit_code min_password_class session_timeout_enabled compiler_installed real_user[] home_directory[] swap_partition[] filesystem_ext[] journal_disk_size journal_coredumps_lastday journal_oldest_bootdate journal_contains_errors swap_partition[] file_systems_ext[] test_category test_group scheduler[] journal_meta_data boot_uefi_booted_secure service_manager running_service_tool binary_paths valid_certificate[] cronjob[] log_directory[] open_logfile[] journal_bootlogs log_rotation_tool log_rotation_config_found auditor deleted_file[] vulnerable_package[] malware_scanner[] file_integrity_tool[] plugin_firewall_iptables_list linux_amount_of_kernels ntp_config_type_startup ntp_config_type_scheduled compiler_world_executable[]);
my @idx4 = qw( ntp_config_type_eventbased ntp_config_type_daemon ntp_config_file[] ntp_config_found ntp_version ntp_unreliable_peer[] postgresql_running linux_auditd_running linux_kernel_io_scheduler nginx_main_conf_file log_file nginx_sub_conf_file[] nginx_config_option[] ssl_tls_protocol_enabled[] systemd systemd_builtin_components systemd_version systemd_status plugin_processes_allprocesses usb_authorized_default_device[] systemd_unit_file[] systemd_unit_not_found[] systemd_service_not_found[] resolv_conf_search_domain[] expired_certificate[] compiler[] fail2ban_config fail2ban_enabled_service[] apache_version apache_module[] resolv_conf_domain redis_running nginx_running open_empty_log_file[] notebook lvm_volume_group[] lvm_volume[] container exception_event[] certificates certificate[] localhost-mapped-to manual_event[] syslog_daemon[] syslog_daemon_present apparmor_enabled apparmor_policy_loaded pam_pwquality selinux_status selinux_mode );
my @idx5 = qw( auth_group_ids_unique auth_group_names_unique );
push @indexes, @idx2, @idx3, @idx4, @idx5;
foreach my $idx ( sort @indexes ) {
delete($lynis_report_data{$idx});
}
if ($pdf) {
require HTML::HTMLDoc;
my $htmlobj = new HTML::HTMLDoc();
$htmlobj->set_input_file($htmldoc);
my $pdfdoc = $htmlobj->generate_pdf();
$pdfdoc->to_file($output);
my $errs = system("rm -f $htmldoc");
if ($verbose) { print "Clean up return code: $errs \n"; }
}
}
if ($verbose) {
print colored("I don't know how to handle these objects yet:\n", "yellow");
print colored(Dumper(\%lynis_report_data), "yellow");
}
###############################################################################
# subs
###############################################################################
sub usage {
if ((!$output) and (!$showversion)) {
unless ($help) {
print colored("You must specify an output file.\n", "yellow");
}
}
print <<END;
$0 -h|--help -v|--verbose -E|--excel -j|--json -x|--xml -p|--pdf -o|--output
Where:
-h|--help Display this useful message, then exit.
-v|--verbose Display more detailed output. This is typically used for debugging, but may provide insight when running into problems.
-i|--input Input log filename. Defaults to /var/log/lynis-report.dat.
-E|--excel Output the report in Microsoft Excel binary format.
-j|--json Output the data in JSON format. It is recommended to pipe to /usr/bin/json_pp for easier (human) reading. Output file name is optional for JSON output.
-x|--xml Output the report as XML.
-p|--pdf Output the report as a PDF. This is simply a copy of the HTML report converted to PDF. Could use refinement.
-o|--output Specifies the output file to print the report to.
END
exit 0;
}
# show script version and gather some relevant data for troubleshooting
sub show_version {
my $uname_a = `uname -a`;
chomp($uname_a);
my $perl_v = `perl --version`;
chomp($perl_v);
print <<EOS;
SCRIPT VERSION: $VERSION
UNAME: $uname_a
OS FullName: $lynis_report_data{'os_fullname'}
OS Version: $lynis_report_data{'os_version'}
Perl Version:
$perl_v
EOS
exit 0;
}
# determine if a number is prime
sub is_prime {
my $num = shift(@_);
my $sqrt = sqrt($num);
my $d = 2;
while (1) {
return 0 if ( $num % $d == 0 );
return 1 if ( $d >= $sqrt );
$d++;
}
}
# deduplicate elements in an array
sub dedup_array {
my $aryref = shift;
my %hash;
foreach my $ele ( @{$aryref} ) { $hash{$ele}++; }
return sort keys(%hash);
}
# calculate the binary "score" for password complexity based off the lynis script findings
sub calc_password_complexity_score {
my ($lc,$uc,$n,$o);
if ($lynis_report_data{'password_max_l_credit'}) { $lc = 0b0001; } else { $lc = 0b0000; }
if ($lynis_report_data{'password_max_u_credit'}) { $uc = 0b0010; } else { $uc = 0b0000; }
if ($lynis_report_data{'password_max_digital_credit'}) { $n = 0b0100; } else { $n = 0b0000; }
if ($lynis_report_data{'password_max_other_credit'}) { $o = 0b1000; } else { $o = 0b0000; }
#printf "%#b\n%#b\n%#b\n%#b\n", $lc, $uc, $n, $o;
my $score = ($lc + $uc + $n + $o);
#printf "%#b\n", $score;
return $score;
}
# give inconsistent keys a value
# some keys may be inconsistent because of OS/Distro version differences
# some keys mey be inconsistent because of the use of plugins
sub pop_inconsistent_keys {
my $fmt = shift;
my $lrd_hash_ref = shift;
my @inconsistent_keys = qw( plugin_firewall_iptables_list notebook container valid_certificate[] usb_authorized_default_device[] expired_certificate[] certificates certificate[] syslog_daemon[] localhost-mapped-to resolv_conf_search_domain[] pam_pwquality malware_scanner[] compiler[] ids_ips_tooling[] fail2ban_config fail2ban_enabled_service[] pam_module[] linux_kernel_io_scheduler[] loaded_kernel_module[] journal_disk_size journal_coredumps_lastday lvm_volume_group[] running_service[] ntp_config_file[] ntp_version ntp_unreliable_peer[] nginx_main_conf_file nginx_sub_conf_file[] log_file nginx_config_option[] ssl_tls_protocol_enabled[] apache_version apache_module[] systemd_version systemd_status systemd_builtin_components systemd_unit_file[] systemd_unit_not_found[] systemd_service_not_found[] installed_packages_array pam_auth_brute_force_protection_module[] vulnerable_package[] plugin_enabled_phase1[] plugin_processes_allprocesses nameserver[] boot_service[] swap_partition[] lvm_volume[] file_systems_ext[] journal_meta_data deleted_file[] license_key pop3_daemon imap_daemon printing_daemon ntp_daemon scheduler[] service_manager running_service_tool cronjob[] apparmor_enabled apparmor_policy_loaded domainname selinux_status selinux_mode );
foreach my $key ( sort @inconsistent_keys ) {
if ($key =~ /(?:notebook|container|apparmor_enabled|apparmor_policy_loaded|selinux_status)/) {
# boolean values
$lrd_hash_ref->{$key} = 0;
} elsif ($key =~ /(?:warning\[\]|running_service\[\])/) {
# these keys expect to be arrays
@{$lrd_hash_ref->{$key}} = qw( "NA" );
} elsif ($key =~ /\bcertificates\b/) {
# these keys expect to be an integer
$lrd_hash_ref->{$key} = 0;
} else {
given ($fmt) {
when (/excel/) { $lrd_hash_ref->{$key} = "NA"; }
when (/json/) { $lrd_hash_ref->{$key} = "NA"; }
default { $lrd_hash_ref->{$key} = "\&nbsp;"; } # covers XML, PDF and HTML (default)
}
}
}
# should operate on the main \%lynis_report_data hash, so we shouldn't need to return anything. Maybe success/fail?
}
# flatten an array
# dedupe it and remove arbitrary elements if legitimate ones exist.
sub flatten {
my @ary = shift;
# check if there's more than one element
if (scalar(@ary) > 1) {
# if so, dedup
@ary = &dedup_array(@ary);
# check again
if (scalar(@ary) > 1) {
# if > 1 check for "NA" or "&nbsp;"
# remove if present
for (my $i = 0; $i<=scalar(@ary); $i++) {
delete $ary[$i] if ($ary[$i] =~ /(?:NA|\&nbsp;)/);
}
# else throw an error, or just return the array (?)
if (scalar(@ary) > 1) { return @ary; }
# if only one, return the scalar.
elsif (scalar(@ary) == 1) { return $ary[0]; }
else { die colored("flatten() array results in 0 elements.", "bold red"); }
} elsif (scalar(@ary) == 1) { return $ary[0]; }
else { die colored("flatten() array results in 0 elements.", "bold red"); }
} elsif (scalar(@ary) == 1) { return $ary[0]; }
else { die colored("flatten() array results in 0 elements.", "bold red"); }
}