From 8c6ab29e659d8281a4d986484bf58d64ee3a080e Mon Sep 17 00:00:00 2001 From: csavelief Date: Wed, 21 Aug 2024 16:57:14 +0200 Subject: [PATCH] [REF. #62] Both the LogAlert and Osquery config files are now generated on a per-server basis. The install script does not depend on git anymore. --- app/Http/Controllers/YnhServerController.php | 2 +- app/Http/Middleware/VerifyCsrfToken.php | 1 + app/Listeners/ConfigureHostListener.php | 2 +- app/Models/YnhOsquery.php | 119 ++++++++++++++---- app/Models/YnhServer.php | 6 +- ..._103002_create_table_ynh_osquery_rules.php | 26 ---- database/seeds/DatabaseSeeder.php | 4 +- routes/web.php | 32 ++++- 8 files changed, 133 insertions(+), 59 deletions(-) diff --git a/app/Http/Controllers/YnhServerController.php b/app/Http/Controllers/YnhServerController.php index 44f7daa..97a6330 100644 --- a/app/Http/Controllers/YnhServerController.php +++ b/app/Http/Controllers/YnhServerController.php @@ -195,7 +195,7 @@ public function installOsquery(YnhServer $server, InstallOsqueryRequest $request $uid = Str::random(10); $ssh = $server->sshConnection($uid, Auth::user()); - if ($server->sshInstallOsquery($ssh)) { + if ($server->sshInstallLogAlertAndOsquery($ssh)) { return response()->json(['success' => "Osquery has been installed!"]); } return response()->json(['error' => "An error occurred."]); diff --git a/app/Http/Middleware/VerifyCsrfToken.php b/app/Http/Middleware/VerifyCsrfToken.php index 48bc6a4..4470668 100644 --- a/app/Http/Middleware/VerifyCsrfToken.php +++ b/app/Http/Middleware/VerifyCsrfToken.php @@ -15,6 +15,7 @@ class VerifyCsrfToken extends Middleware '/setup/token', '/setup/script', '/logalert/*', + '/osquery/*', '/metrics', ]; } diff --git a/app/Listeners/ConfigureHostListener.php b/app/Listeners/ConfigureHostListener.php index 9f96f3f..0dd4e10 100644 --- a/app/Listeners/ConfigureHostListener.php +++ b/app/Listeners/ConfigureHostListener.php @@ -42,7 +42,7 @@ protected function handle2($event) $server->save(); $ssh = $server->sshConnection($uid, $user); - $isOk = $isOk && $server->sshInstallOsquery($ssh); + $isOk = $isOk && $server->sshInstallLogAlertAndOsquery($ssh); } // $ports = [25 /* email */, 389 /* ldap */, 587 /* email */, 853 /* ? */, 993 /* email */, 5222 /* xmpp client */, 5269 /* xmpp server */]; diff --git a/app/Models/YnhOsquery.php b/app/Models/YnhOsquery.php index d482245..f55bd4d 100644 --- a/app/Models/YnhOsquery.php +++ b/app/Models/YnhOsquery.php @@ -36,34 +36,101 @@ class YnhOsquery extends Model 'packed' => 'boolean', ]; - public static function installOsquery(YnhServer $server): string + public static function configLogAlert(YnhServer $server): array + { + return ["monitors" => [ + [ + "name" => "Monitor Osquery Daemon Output", + "path" => "/var/log/osquery/osqueryd.*.log", + "match" => ".*", + "regexp" => true, + "url" => "https://app.towerify.io/logalert/{$server->secret}" + ] + ], + "sleep" => 5, + "echo" => false, + "verbose" => 1 + ]; + } + + public static function configOsquery(): array + { + $schedule = []; + YnhOsqueryRule::orderBy('name', 'asc') + ->get() + ->each(function (YnhOsqueryRule $rule) use (&$schedule) { + $schedule[$rule->name] = [ + 'query' => $rule->query, + 'interval' => $rule->interval, + 'removed' => $rule->removed, + 'snapshot' => $rule->snapshot, + 'platform' => $rule->platform->value, + ]; + }); + return [ + "options" => [ + "logger_snapshot_event_type" => "true", + "schedule_splay_percent" => 10 + ], + "platform" => "linux", + "schedule" => $schedule, + "file_paths" => [ + "configuration" => [ + "/etc/passwd", + "/etc/shadow", + "/etc/ld.so.preload", + "/etc/ld.so.conf", + "/etc/ld.so.conf.d/%%", + "/etc/pam.d/%%", + "/etc/resolv.conf", + "/etc/rc%/%%", + "/etc/my.cnf", + "/etc/modules", + "/etc/hosts", + "/etc/hostname", + "/etc/fstab", + "/etc/crontab", + "/etc/cron%/%%", + "/etc/init/%%", + "/etc/rsyslog.conf" + ], + "binaries" => [ + "/usr/bin/%%", + "/usr/sbin/%%", + "/bin/%%", + "/sbin/%%", + "/usr/local/bin/%%", + "/usr/local/sbin/%%" + ] + ], + "events" => [ + "disable_subscribers" => [ + "user_events" + ] + ], + "packs" => [], + ]; + } + + public static function installLogAlertAndOsquery(YnhServer $server): string { return <</opt/logalert/logalert.bin chmod 755 /opt/logalert/logalert.bin - - # Set base config - echo '{"monitors":[{"name":"Monitor Osquery Daemon Output","path":"/var/log/osquery/osqueryd.*.log","match":".*","regexp":true,"url":"https://app.towerify.io/logalert/{$server->secret}"}],"sleep":5,"echo":true,"verbose":1}' >/opt/logalert/config.json fi # LEGACY CODE BEGINS HERE @@ -82,17 +149,19 @@ public static function installOsquery(YnhServer $server): string sudo -H -u root bash -c 'tmux kill-ses -t logalert' osqueryctl stop osqueryd -# Update Osquery's rules -cat /etc/osquery/osquery.conf | \ - jq $'del(.schedule.socket_events)' | \ - jq $'del(.schedule.network_interfaces_snapshot)' | \ - jq $'del(.schedule.process_events)' | \ - jq $'.schedule.packages_available_snapshot += {query:"SELECT name, version, source FROM deb_packages;",interval:86400,snapshot:true}' | \ - jq $'.schedule.memory_available_snapshot += {query:"select printf(\'%.2f\',((memory_total - memory_available) * 1.0)/1073741824) as used_space_gb, printf(\'%.2f\',(1.0 * memory_available / 1073741824)) as space_left_gb, printf(\'%.2f\',(1.0 * memory_total / 1073741824)) as total_space_gb, printf(\'%.2f\',(((memory_total - memory_available) * 1.0)/1073741824)/(1.0 * memory_total / 1073741824)) * 100 as \'%_used\', printf(\'%.2f\',(1.0 * memory_available / 1073741824)/(1.0 * memory_total / 1073741824)) * 100 as \'%_available\' from memory_info;",interval:300,snapshot:true}' | \ - jq $'.schedule.disk_available_snapshot += {query:"select printf(\'%.2f\',((blocks - blocks_available * 1.0) * blocks_size)/1073741824) as used_space_gb, printf(\'%.2f\',(1.0 * blocks_available * blocks_size / 1073741824)) as space_left_gb, printf(\'%.2f\',(1.0 * blocks * blocks_size / 1073741824)) as total_space_gb, printf(\'%.2f\',(((blocks - blocks_available * 1.0) * blocks_size)/1073741824)/(1.0 * blocks * blocks_size / 1073741824)) * 100 as \'%_used\', printf(\'%.2f\',(1.0 * blocks_available * blocks_size / 1073741824)/(1.0 * blocks * blocks_size / 1073741824)) * 100 as \'%_available\' from mounts where path = \'/\';",interval:300,snapshot:true}' \ - >/etc/osquery/osquery2.conf - -mv -f /etc/osquery/osquery2.conf /etc/osquery/osquery.conf +# Update LogAlert configuration +wget -O /opt/logalert/config2.json https://app.towerify.io/logalert/{$server->secret} + +if jq empty /opt/logalert/config2.json; then + mv -f /opt/logalert/config2.json /opt/logalert/config.json +fi + +# Update Osquery configuration +wget -O /etc/osquery/osquery2.conf https://app.towerify.io/osquery/{$server->secret} + +if jq empty /etc/osquery/osquery2.conf; then + mv -f /etc/osquery/osquery2.conf /etc/osquery/osquery.conf +fi # Drop Osquery daemon's output every sunday at 01:00 am cat <(fgrep -i -v 'rm /var/log/osquery/osqueryd.results.log /var/log/osquery/osqueryd.snapshots.log' <(crontab -l)) <(echo '0 1 * * 0 rm /var/log/osquery/osqueryd.results.log /var/log/osquery/osqueryd.snapshots.log') | crontab - diff --git a/app/Models/YnhServer.php b/app/Models/YnhServer.php index 044c032..b8da70b 100644 --- a/app/Models/YnhServer.php +++ b/app/Models/YnhServer.php @@ -363,9 +363,9 @@ public function sshTestConnection(): bool return $this->sshKeyPair()->isSshConnectionUpAndRunning($this->ip(), $this->ssh_port, $this->ssh_username); } - public function sshInstallOsquery(SshConnection2 $ssh) + public function sshInstallLogAlertAndOsquery(SshConnection2 $ssh): bool { - $installScript = YnhOsquery::installOsquery($this); + $installScript = YnhOsquery::installLogAlertAndOsquery($this); $ssh->newTrace(SshTraceStateEnum::IN_PROGRESS, 'Installing Osquery...'); $filename = 'install-yunohost-' . Str::random(10); $isOk = $ssh->upload($filename, $installScript); @@ -376,7 +376,7 @@ public function sshInstallOsquery(SshConnection2 $ssh) return $isOk; } - public function sshInstallYunoHost(SshConnection2 $ssh, string $domain, string $username) + public function sshInstallYunoHost(SshConnection2 $ssh, string $domain, string $username): bool { $password = Str::random(30); $installScript = <<value, ])->default(\App\Enums\OsqueryPlatformEnum::ALL->value); }); - Schema::create('ynh_osquery_rules_scope_customer', function (Blueprint $table) { - - $table->id(); - $table->timestamps(); - - // Link a rule to a customer - $table->intOrBigIntBasedOnRelated('rule_id', Schema::connection(null), 'ynh_osquery_rules.id')->cascadeOnDelete(); - $table->foreign('rule_id')->references('id')->on('ynh_osquery_rules')->cascadeOnDelete(); - - $table->intOrBigIntBasedOnRelated('customer_id', Schema::connection(null), 'customers.id')->cascadeOnDelete(); - $table->foreign('customer_id')->references('id')->on('customers')->cascadeOnDelete(); - }); - Schema::create('ynh_osquery_rules_scope_tenant', function (Blueprint $table) { - - $table->id(); - $table->timestamps(); - - // Link a rule to a tenant - $table->intOrBigIntBasedOnRelated('rule_id', Schema::connection(null), 'ynh_osquery_rules.id')->cascadeOnDelete(); - $table->foreign('rule_id')->references('id')->on('ynh_osquery_rules')->cascadeOnDelete(); - - $table->intOrBigIntBasedOnRelated('tenant_id', Schema::connection(null), 'tenants.id')->cascadeOnDelete(); - $table->foreign('tenant_id')->references('id')->on('tenants')->cascadeOnDelete(); - }); } /** @@ -69,8 +45,6 @@ public function up() */ public function down() { - Schema::dropIfExists('ynh_osquery_rules_scope_tenant'); - Schema::dropIfExists('ynh_osquery_rules_scope_customer'); Schema::dropIfExists('ynh_osquery_rules'); } }; diff --git a/database/seeds/DatabaseSeeder.php b/database/seeds/DatabaseSeeder.php index fda7c69..950e9f8 100644 --- a/database/seeds/DatabaseSeeder.php +++ b/database/seeds/DatabaseSeeder.php @@ -251,7 +251,7 @@ private function setupProducts(): void private function setupOsqueryRules(): void { - $rules = $this->palantirOsqueryRulesForLinux()['schedule']; + $rules = $this->palantirOsqueryRules()['schedule']; foreach ($rules as $name => $rule) { $fields = []; if (isset($rule['description'])) { @@ -322,7 +322,7 @@ private function customRules(): array ]]; } - private function palantirOsqueryRulesForLinux(): array + private function palantirOsqueryRules(): array { // See https://github.com/palantir/osquery-configuration/blob/master/Classic/Servers/Linux/osquery.conf for details $rules = <<< EOF diff --git a/routes/web.php b/routes/web.php index 0c2996d..2526f18 100644 --- a/routes/web.php +++ b/routes/web.php @@ -170,7 +170,7 @@ // 3.2 chmod +x install.sh // 3.3 ./install.sh // 3.4 rm install.sh - $installScript = \App\Models\YnhOsquery::installOsquery($server); + $installScript = \App\Models\YnhOsquery::installLogAlertAndOsquery($server); return response($installScript, 200) ->header('Content-Type', 'text/plain'); @@ -228,6 +228,36 @@ return new JsonResponse(['status' => 'success'], 200, ['Access-Control-Allow-Origin' => '*']); }); +Route::get('/logalert/{secret}', function (string $secret, \Illuminate\Http\Request $request) { + + $server = \App\Models\YnhServer::where('secret', $secret)->first(); + + if (!$server) { + return response('Unknown server', 500) + ->header('Content-Type', 'text/plain'); + } + + $config = json_encode(\App\Models\YnhOsquery::configLogAlert($server)); + + return response($config, 200) + ->header('Content-Type', 'text/plain'); +}); + +Route::get('/osquery/{secret}', function (string $secret, \Illuminate\Http\Request $request) { + + $server = \App\Models\YnhServer::where('secret', $secret)->first(); + + if (!$server) { + return response('Unknown server', 500) + ->header('Content-Type', 'text/plain'); + } + + $config = json_encode(\App\Models\YnhOsquery::configOsquery()); + + return response($config, 200) + ->header('Content-Type', 'text/plain'); +}); + /** @deprecated */ Route::post('metrics', function (\Illuminate\Http\Request $request) {