from flask import Flask, request import socket from datetime import datetime import json app = Flask(__name__) SYSLOG_SERVER = 'syslog.server.address' # Replace with your syslog server address SYSLOG_PORT = 6675 # Replace with your syslog server port FACILITY = 13 # 1 - User-level messages, 13 - Security audit log SEVERITY = 2 # 1 - Alert, 2 - Critical, 3 - Error, etc. MAX_SYSLOG_MSG = 900 # safe UDP syslog payload size MAX_FILE_COUNT = 5 # how many file paths to keep MAX_COMMENT_LENGTH = 100 # max chars per comment def get_host_ip(): try: host_name = socket.gethostname() host_ip = socket.gethostbyname(host_name) return host_ip except Exception: return "Unable to get Host IP" def truncate_payload(payload: dict) -> dict: """Return a safe-to-send version of the payload with large fields trimmed.""" safe_payload = payload.copy() # Truncate files list if present if "files" in safe_payload and isinstance(safe_payload["files"], list): files = safe_payload["files"] if len(files) > MAX_FILE_COUNT: safe_payload["files"] = files[:MAX_FILE_COUNT] safe_payload["files_truncated_count"] = len(files) - MAX_FILE_COUNT # Truncate comments in actions list if "actions" in safe_payload and isinstance(safe_payload["actions"], list): new_actions = [] for action in safe_payload["actions"]: act = action.copy() if "comment" in act and isinstance(act["comment"], str): if len(act["comment"]) > MAX_COMMENT_LENGTH: act["comment"] = act["comment"][:MAX_COMMENT_LENGTH] + "...[truncated]" new_actions.append(act) safe_payload["actions"] = new_actions return safe_payload def format_uef(payload, source_ip): # Static fields device_product = "Eyeglass Zero Trust" device_vendor = "Superna" device_version = "V1" device_event_class_id = "security" version = "1.0" event_type = "threat_detection" # Key dynamic fields client_ip = payload.get('clientIPs', ['Unknown'])[0] user = payload.get('userName', 'Unknown') severity = payload.get('severity', 'Unknown') action = payload.get('actions', [{}])[0].get('action', 'Unknown') # Truncate payload safely safe_payload = truncate_payload(payload) custom_data = json.dumps(safe_payload, default=str) uef_message = ( f"time={datetime.utcnow().isoformat()}Z " f"client_ip={client_ip} user={user} severity={severity} action={action} " f"device_product={device_product} device_vendor={device_vendor} " f"device_version={device_version} event_type={event_type} " f"custom_data={custom_data}" ) # Enforce syslog size limit if len(uef_message.encode("utf-8")) > MAX_SYSLOG_MSG: uef_message = uef_message.encode("utf-8")[:MAX_SYSLOG_MSG].decode("utf-8", "ignore") + "...[cut]" return uef_message def send_to_syslog(uef_message, source_ip): priority = FACILITY * 8 + SEVERITY syslog_header = f"<{priority}>1 {datetime.utcnow().isoformat()} eyeglass-vm Superna-Zero-Trust {source_ip} -" syslog_message = f"{syslog_header} {uef_message}" sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.sendto(syslog_message.encode('utf-8'), (SYSLOG_SERVER, SYSLOG_PORT)) sock.close() @app.route('/webhook', methods=['POST']) def webhook(): try: payload = request.json source_ip = get_host_ip() uef_message = format_uef(payload, source_ip) send_to_syslog(uef_message, source_ip) return "Success", 200 except Exception as e: return str(e), 500 if __name__ == '__main__': app.run(host='0.0.0.0', port=5000, debug=True)