Automate OpenCanary definitions, localize times
This commit is contained in:
147
installer.sh
147
installer.sh
@ -234,6 +234,7 @@ INSERT OR IGNORE INTO settings(key, value) VALUES('active_profile', '');
|
||||
INSERT OR IGNORE INTO settings(key, value) VALUES('admin_port', '');
|
||||
INSERT OR IGNORE INTO settings(key, value) VALUES('admin_expires_at', '');
|
||||
INSERT OR IGNORE INTO settings(key, value) VALUES('appliance_name', 'BaldCanary Appliance');
|
||||
INSERT OR IGNORE INTO settings(key, value) VALUES('timezone', '');
|
||||
SQL
|
||||
|
||||
chown -R "$APP_USER:$APP_GROUP" "$APP_ROOT/db"
|
||||
@ -241,6 +242,73 @@ chmod 775 "$APP_ROOT/db"
|
||||
chmod 660 "$APP_DB"
|
||||
}
|
||||
|
||||
write_opencanary_event_map() {
|
||||
log "Generating OpenCanary event label map..."
|
||||
|
||||
"$APP_ROOT/venv/bin/python" <<'PY'
|
||||
import inspect
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
from opencanary import logger
|
||||
|
||||
out = {}
|
||||
|
||||
def humanize(name: str) -> str:
|
||||
name = re.sub(r'^LOG_', '', name)
|
||||
parts = name.split('_')
|
||||
|
||||
pretty = []
|
||||
acronyms = {
|
||||
'FTP', 'HTTP', 'HTTPS', 'SSH', 'SMB', 'MYSQL', 'MSSQL',
|
||||
'RDP', 'VNC', 'NTP', 'SNMP', 'TFTP', 'TCP', 'UDP',
|
||||
'DNS', 'LDAP', 'REDIS', 'SIP'
|
||||
}
|
||||
|
||||
for part in parts:
|
||||
if part in acronyms:
|
||||
pretty.append(part)
|
||||
elif part == 'LOGIN':
|
||||
pretty.append('Login')
|
||||
elif part == 'ATTEMPT':
|
||||
pretty.append('Attempt')
|
||||
elif part == 'CONNECTION':
|
||||
pretty.append('Connection')
|
||||
elif part == 'NEW':
|
||||
pretty.append('New')
|
||||
else:
|
||||
pretty.append(part.capitalize())
|
||||
|
||||
label = ' '.join(pretty)
|
||||
|
||||
# A few nicer aliases.
|
||||
label = label.replace('RDP Connection Made', 'RDP Connection')
|
||||
label = label.replace('HTTP GET', 'HTTP GET Request')
|
||||
label = label.replace('HTTP POST', 'HTTP POST Request')
|
||||
|
||||
return label
|
||||
|
||||
for cls_name in ('CanaryLogger', 'PyLogger'):
|
||||
cls = getattr(logger, cls_name, None)
|
||||
if not cls:
|
||||
continue
|
||||
|
||||
for name, value in inspect.getmembers(cls):
|
||||
if not name.startswith('LOG_'):
|
||||
continue
|
||||
if isinstance(value, int):
|
||||
out[str(value)] = humanize(name)
|
||||
|
||||
Path('/opt/baldcanary/config/opencanary_event_labels.json').write_text(
|
||||
json.dumps(out, indent=2, sort_keys=True)
|
||||
)
|
||||
PY
|
||||
|
||||
chown "$APP_USER:$APP_GROUP" "$APP_ROOT/config/opencanary_event_labels.json"
|
||||
chmod 664 "$APP_ROOT/config/opencanary_event_labels.json"
|
||||
}
|
||||
|
||||
write_common_php() {
|
||||
log "Writing shared PHP helpers..."
|
||||
|
||||
@ -273,6 +341,78 @@ function bc_set_setting(string $key, string $value): void {
|
||||
ON CONFLICT(key) DO UPDATE SET value=excluded.value, updated_at=CURRENT_TIMESTAMP');
|
||||
$stmt->execute([$key, $value]);
|
||||
}
|
||||
|
||||
function bc_local_timezone(): string {
|
||||
$tz = bc_setting('timezone', '');
|
||||
if ($tz !== '') {
|
||||
return $tz;
|
||||
}
|
||||
|
||||
$systemTz = trim((string)@shell_exec('timedatectl show -p Timezone --value 2>/dev/null'));
|
||||
if ($systemTz !== '') {
|
||||
return $systemTz;
|
||||
}
|
||||
|
||||
return date_default_timezone_get() ?: 'UTC';
|
||||
}
|
||||
|
||||
function bc_local_time(?string $utcTime): string {
|
||||
$utcTime = trim((string)$utcTime);
|
||||
if ($utcTime === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
try {
|
||||
$dt = new DateTime($utcTime, new DateTimeZone('UTC'));
|
||||
$dt->setTimezone(new DateTimeZone(bc_local_timezone()));
|
||||
return $dt->format('F j, Y g:i:s A');
|
||||
} catch (Throwable $e) {
|
||||
return $utcTime;
|
||||
}
|
||||
}
|
||||
|
||||
function bc_event_label(?string $type): string {
|
||||
$type = trim((string)$type);
|
||||
|
||||
$labels = [
|
||||
// BaldCanary web events
|
||||
'page_view' => 'Page View',
|
||||
'form_submit' => 'Form Submission',
|
||||
'xss_probe' => 'XSS Probe',
|
||||
'sql_injection_probe' => 'SQL Injection Probe',
|
||||
'command_injection_probe' => 'Command Injection Probe',
|
||||
'path_traversal_probe' => 'Path Traversal Probe',
|
||||
'sensitive_file_probe' => 'Sensitive File Probe',
|
||||
'session_file_probe' => 'Session File Probe',
|
||||
'exposed_session_directory' => 'Exposed Session Directory',
|
||||
'php_session_file' => 'PHP Session File Access',
|
||||
'backup_directory' => 'Backup Directory Access',
|
||||
'mysql_backup_directory' => 'MySQL Backup Directory Access',
|
||||
'api_docs' => 'API Documentation Access',
|
||||
'swagger_docs' => 'Swagger Documentation Access',
|
||||
'phpinfo_probe' => 'PHP Info Probe',
|
||||
'env_file_probe' => 'Environment File Probe',
|
||||
'config_file_probe' => 'Config File Probe',
|
||||
];
|
||||
|
||||
$mapFile = '/opt/baldcanary/config/opencanary_event_labels.json';
|
||||
if (is_readable($mapFile)) {
|
||||
$oc = json_decode((string)file_get_contents($mapFile), true);
|
||||
if (is_array($oc)) {
|
||||
$labels = $labels + $oc;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($labels[$type])) {
|
||||
return $labels[$type];
|
||||
}
|
||||
|
||||
if (preg_match('/^[a-z0-9_\-\.]+$/i', $type)) {
|
||||
return ucwords(str_replace(['_', '-', '.'], ' ', $type));
|
||||
}
|
||||
|
||||
return $type !== '' ? $type : 'Unknown Event';
|
||||
}
|
||||
PHP
|
||||
|
||||
cat > "$APP_ROOT/app/common/functions.php" <<'PHP'
|
||||
@ -596,7 +736,7 @@ $webhooks = $db->query('SELECT * FROM webhook_targets ORDER BY id DESC')->fetchA
|
||||
<tbody>
|
||||
<?php foreach ($events as $event): ?>
|
||||
<tr>
|
||||
<td><?=h($event['event_time'])?></td>
|
||||
<td><?=h(bc_local_time($event['event_time']))?></td>
|
||||
<td><span class="badge text-bg-secondary"><?=h($event['severity'])?></span></td>
|
||||
<td><?=h(bc_event_label($event['event_type']))?></td>
|
||||
<td><?=h($event['src_ip'])?></td>
|
||||
@ -628,7 +768,7 @@ $profile = bc_setting('active_profile', 'Not set');
|
||||
body{font-family:Arial,sans-serif;margin:40px;color:#222}.cover{border-bottom:4px solid #222;margin-bottom:24px;padding-bottom:24px}.card{display:inline-block;border:1px solid #ddd;border-radius:10px;padding:16px;margin:8px 8px 8px 0;min-width:160px}.small{color:#666;font-size:12px}table{border-collapse:collapse;width:100%;margin-top:20px}th,td{border-bottom:1px solid #ddd;text-align:left;padding:8px;font-size:12px}th{background:#f1f1f1}@media print{button{display:none}}
|
||||
</style></head><body>
|
||||
<button onclick="window.print()">Print / Save as PDF</button>
|
||||
<div class="cover"><h1>BaldCanary Pentest Validation Report</h1><p>Generated: <?=h(date('F j, Y g:i A'))?></p><p>Active profile: <strong><?=h($profile)?></strong></p></div>
|
||||
<div class="cover"><h1>BaldCanary Pentest Validation Report</h1><p>Generated: <?=h((new DateTime('now', new DateTimeZone(bc_local_timezone())))->format('F j, Y g:i A'))?></p><p>Active profile: <strong><?=h($profile)?></strong></p></div>
|
||||
<div class="card"><div class="small">Total Events</div><h2><?=count($events)?></h2></div>
|
||||
<div class="card"><div class="small">Unique Source IPs</div><h2><?=count(array_unique(array_filter(array_column($events,'src_ip'))))?></h2></div>
|
||||
<div class="card"><div class="small">High Interest Events</div><h2><?=count(array_filter($events, fn($e)=>in_array($e['severity'],['high','critical'],true)))?></h2></div>
|
||||
@ -636,7 +776,7 @@ body{font-family:Arial,sans-serif;margin:40px;color:#222}.cover{border-bottom:4p
|
||||
<p>BaldCanary recorded interaction with the selected deception profile and exposed canary services. Events below may indicate penetration test activity, unauthorized curiosity, automated scanning, or attempted exploitation.</p>
|
||||
<h2>Event Timeline</h2>
|
||||
<table><thead><tr><th>Time</th><th>Severity</th><th>Type</th><th>Source IP</th><th>Method</th><th>Path</th><th>Bait</th></tr></thead><tbody>
|
||||
<?php foreach ($events as $e): ?><tr><td><?=h($e['event_time'])?></td><td><?=h($e['severity'])?></td><td><?=h(bc_event_label($e['event_type']))?></td><td><?=h($e['src_ip'])?></td><td><?=h($e['method'])?></td><td><?=h($e['path'])?></td><td><?=h($e['matched_bait'])?></td></tr><?php endforeach; ?>
|
||||
<?php foreach ($events as $e): ?><tr><td><?=h(bc_local_time($e['event_time']))?></td><td><?=h($e['severity'])?></td><td><?=h(bc_event_label($e['event_type']))?></td><td><?=h($e['src_ip'])?></td><td><?=h($e['method'])?></td><td><?=h($e['path'])?></td><td><?=h($e['matched_bait'])?></td></tr><?php endforeach; ?>
|
||||
</tbody></table>
|
||||
</body></html>
|
||||
PHP
|
||||
@ -1415,6 +1555,7 @@ main() {
|
||||
install_packages
|
||||
create_user_and_dirs
|
||||
install_python_env
|
||||
write_opencanary_event_map
|
||||
init_sqlite
|
||||
write_common_php
|
||||
write_admin_app
|
||||
|
||||
Reference in New Issue
Block a user