From ba2c63acdc545fdc0a25013e9738f5411ea4cfcd Mon Sep 17 00:00:00 2001 From: baldnerd Date: Fri, 8 May 2026 16:11:47 -0400 Subject: [PATCH] Automate OpenCanary definitions, localize times --- installer.sh | 147 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 144 insertions(+), 3 deletions(-) diff --git a/installer.sh b/installer.sh index 81b2619..daeeca0 100755 --- a/installer.sh +++ b/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 - + @@ -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}} -

BaldCanary Pentest Validation Report

Generated:

Active profile:

+

BaldCanary Pentest Validation Report

Generated: format('F j, Y g:i A'))?>

Active profile:

Total Events

Unique Source IPs

High Interest Events

in_array($e['severity'],['high','critical'],true)))?>

@@ -636,7 +776,7 @@ body{font-family:Arial,sans-serif;margin:40px;color:#222}.cover{border-bottom:4p

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.

Event Timeline

- +
TimeSeverityTypeSource IPMethodPathBait
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