#!/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 = ) { 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 = " "; } # 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 =~ /(?:\ \;|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 < lynis report

lynis Asset Report

created by lynis_report

lynis infohost info network infosecurity Info boot infokernel info
filesystem/journalling infoservice info installed packages

host findings:

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"; } when (($lynis_report_data{'hardening_index'} <= 90) and ($lynis_report_data{'hardening_index'} > 80)) { # yellow print OUT "\t\t\t\t"; } when (($lynis_report_data{'hardening_index'} <= 80) and ($lynis_report_data{'hardening_index'} > 65)) { # orange print OUT "\t\t\t\t"; } when ($lynis_report_data{'hardening_index'} <= 65) { # red print OUT "\t\t\t\t"; } default { # error } } print OUT "\t\t\t
hardening index:$lynis_report_data{'hardening_index'}$lynis_report_data{'hardening_index'}$lynis_report_data{'hardening_index'}$lynis_report_data{'hardening_index'}
Auditor:$lynis_report_data{'auditor'}
\n"; # handle "exception events" if (exists($lynis_report_data{'exception_event[]'})) { print OUT "

exceptions!

\n"; print OUT "
\n"; print OUT "
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.

\n"; if (ref($lynis_report_data{'exception_event[]'}) eq 'ARRAY') { print OUT "\n"; foreach my $exp ( @{$lynis_report_data{'exception_event[]'}} ) { print OUT "\n"; } print OUT "
$exp
\n
\n"; } else { print OUT "
$lynis_report_data{'exception_event[]'}
\n"; } } # warnings if (!exists($lynis_report_data{'warning[]'})) { print OUT "

warnings (0):

\n"; } else { print OUT "

warnings (".scalar(@{$lynis_report_data{'warning[]'}})."):

\n"; } print OUT < 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\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\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 if ((ref($lynis_report_data{'suggestion[]'}) eq 'ARRAY') and (${$lynis_report_data{'suggestion[]'}}[0] =~ /\|/)) { print OUT "\t\t\t

suggestions (".scalar(@{$lynis_report_data{'suggestion[]'}})."):

\n"; } else { print OUT "\t\t\t

suggestions (0):

\n"; } print OUT <
Test IDDescriptionDetailsSolution
$warn_id$warn_desc$to_long_severity{$warn_sev}$warn_f4
$warn_id$warn_desc$to_long_severity{$warn_sev}$warn_f4
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"; print OUT ""; print OUT ""; print OUT "\n"; } } print OUT <

manual checks:

    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
  • $man
  • \n"; } } print OUT <
    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
    deleted files (".scalar(@{$lynis_report_data{'deleted_file[]'}})."):
    \n"; if (scalar(@{$lynis_report_data{'deleted_file[]'}}) < 10) { print OUT "\t\t\t\t\n"; } foreach my $f ( @{$lynis_report_data{'deleted_file[]'}} ) { print OUT "\t\t\t\t\t
Test IDDescriptionDetailsSolution
$sug_id$sug_desc".($sug_sev ? $sug_sev : " ")."".($sug_f4 ? $sug_f4 : " ")."
END print OUT "\t\t\t\t\t\n"; print OUT "\t\t\t\t\t\n"; print OUT < END if (($lynis_report_data{'lynis_update_available'} == 0) or ($lynis_report_data{'lynis_update_available'} == 1)) { print OUT "\t\t\t\t\t\n"; } elsif ($lynis_report_data{'lynis_update_available'} == -1) { print OUT "\t\t\t\t\t\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\n"; print OUT < END print OUT "\t\t\t\t\t\t\n"; print OUT < END if (ref($lynis_report_data{'plugin_firewall_iptables_list'}) eq 'ARRAY') { print OUT "\t\t\t\t\t\n"; } print OUT "\t\t\t\t
lynis version:$lynis_report_data{'lynis_version'}lynis tests done:$lynis_report_data{'lynis_tests_done'}
lynis update available:$to_bool{$lynis_report_data{'lynis_update_available'}}lynis update available:N/A - There was an unexpected error trying to retrieve update status.license key:$lynis_report_data{'license_key'}
report version:$lynis_report_data{'report_version_major'}.$lynis_report_data{'report_version_minor'}
test category:$lynis_report_data{'test_category'} test group:$lynis_report_data{'test_group'}
number of plugins enabled:$lynis_report_data{'plugins_enabled'} plugin directory:$lynis_report_data{'plugin_directory'}
phase 1 plugins enabled:\n"; print OUT "\t\t\t\t\t\t\t\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\n"; } } } print OUT "\t\t\t\t\t\t\t
name:$nversion:$v
\n"; print OUT "\t\t\t\t\t\t
report start time:$lynis_report_data{'report_datetime_start'}report end time:$lynis_report_data{'report_datetime_end'}
hostid:$lynis_report_data{'hostid'}
hostid:$lynis_report_data{'hostid2'}
Plugin-firewall iptables list:".join("
\n", @{$lynis_report_data{'plugin_firewall_iptables_list'}})."
\n"; if ((exists($lynis_report_data{'plugin_processes_allprocesses'})) and ($lynis_report_data{'plugin_processes_allprocesses'} ne "")) { print OUT "\t\t\t\t
Plugin-processes: discovered processes:
\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\n"; } foreach my $p ( sort @{$lynis_report_data{'plugin_processes_allprocesses'}} ) { print OUT "\t\t\t\t\t\t