Add shadow security finding inbox
CI / Rust checks (push) Canceled after 0s
CI / Docs and registry checks (push) Canceled after 0s
CI / Smoke checks (push) Canceled after 0s
Coverage / Coverage baseline (push) Canceled after 0s
Security / Cargo audit (push) Canceled after 0s
Security / Cargo deny (push) Canceled after 0s
Security / Secret pattern check (push) Canceled after 0s
Security / Dependency review (push) Canceled after 0s

This commit is contained in:
igor04091968
2026-07-01 06:12:08 +03:00
parent fe87c85a31
commit 757fd3125d
27 changed files with 5524 additions and 19 deletions
+47 -1
View File
@@ -79,6 +79,13 @@ Behavior:
- optional `*.caseid` sidecar with the same basename triggers automatic bounded case linkage
- processed `*.zip` is moved out of `drop/` into `report_dir/input-drop/` to avoid repeated re-trigger loops
- sidecars are archived into `report_dir/input-sidecars/`
- bad drop packages are rejected before `accept`, moved to
`/opt/hayabusa/quarantine/drop/<timestamp>_<package>/`, and recorded with a
`reason.json` file instead of blocking later packages
- bad or partially extracted incoming packages are moved to
`/opt/hayabusa/quarantine/incoming/<timestamp>_<package>/`; `process-inbox`
continues with the remaining queue and does not trip systemd start-limit only
because of one poison archive
## Windows direct upload into the drop zone
@@ -118,4 +125,43 @@ Production scheduled task on `SHARKON2025`:
Do not switch this task back to `SYSTEM` on the current RDP host: Task Scheduler starts `powershell.exe` under `SYSTEM`, but the process exits with `0xC0000142` before the upload script starts.
Server-side processing accepts Windows zip packages with backslash path separators and UTF-8 BOM in sidecar JSON. `aw-hayabusa-autoprocess` processes the full incoming queue after accepting a drop package, so stale incoming files from an earlier failed run are drained before the latest intake is recorded.
Server-side processing accepts Windows zip packages with backslash path
separators and UTF-8 BOM in sidecar JSON. `aw-hayabusa-autoprocess` processes
the full incoming queue after accepting a drop package, so stale incoming files
from an earlier failed run are drained before the latest intake is recorded.
Poison-package handling is fail-closed:
- Rust `aw-hayabusa-autoprocess-rust` validates the zip and sidecars before
calling `aw-hayabusa accept`.
- A corrupt/empty/unsafe drop package is quarantined with its `.meta.json`,
`.caseid`, optional checksum sidecar and `reason.json`.
- `aw-hayabusa process-inbox` isolates a failed incoming package instead of
aborting the whole batch.
- Operators replay only a fixed/re-exported package by moving it back to the
drop zone or incoming queue. Do not edit quarantined evidence in place.
## Security Finding Inbox integration
`aw-hayabusa-autoprocess-rust` can publish a normalized suspicious-workstation
finding after a successful intake is written to `/opt/hayabusa/state/latest-intake.json`.
Default is disabled to keep forensic processing independent from ClickHouse:
```bash
AW_SECURITY_FINDING_INBOX_ENABLED=false
```
Enable after the ClickHouse schema and CLI are installed:
```bash
AW_SECURITY_FINDING_INBOX_ENABLED=true
AW_SECURITY_FINDING_INBOX_BIN=/usr/local/bin/security-finding-inbox
AW_SECURITY_FINDING_INBOX_MIN_SEVERITY=medium
AW_SECURITY_FINDING_INBOX_REQUIRED=false
```
With `AW_SECURITY_FINDING_INBOX_REQUIRED=false`, a temporary ClickHouse/inbox
failure is logged as warning and does not poison the Hayabusa backlog. Use
`true` only when the operator wants inbox publication failure to become an
operational failure for the drop service.
+60 -6
View File
@@ -14,6 +14,7 @@ HAYA_STAGING_DIR="${AW_HAYABUSA_STAGING_DIR:-${HAYA_ROOT}/inbox/staging}"
HAYA_ARCHIVE_PACKAGES_DIR="${AW_HAYABUSA_ARCHIVE_PACKAGES_DIR:-${HAYA_ROOT}/archive/packages}"
HAYA_ARCHIVE_EXTRACTED_DIR="${AW_HAYABUSA_ARCHIVE_EXTRACTED_DIR:-${HAYA_ROOT}/archive/extracted}"
HAYA_LOGS_DIR="${AW_HAYABUSA_LOGS_DIR:-${HAYA_ROOT}/state/logs}"
HAYA_QUARANTINE_DIR="${AW_HAYABUSA_QUARANTINE_DIR:-${HAYA_ROOT}/quarantine/incoming}"
LAST_REPORT_DIR=""
usage() {
@@ -57,7 +58,8 @@ ensure_layout() {
"${HAYA_INCOMING_DIR}" \
"${HAYA_STAGING_DIR}" \
"${HAYA_ARCHIVE_PACKAGES_DIR}" \
"${HAYA_ARCHIVE_EXTRACTED_DIR}"
"${HAYA_ARCHIVE_EXTRACTED_DIR}" \
"${HAYA_QUARANTINE_DIR}"
}
run_logged() {
@@ -405,7 +407,8 @@ process_one_package() {
package_sha256="$(sha256sum "${package_path}" | awk '{print $1}')"
if ! extract_zip_normalized "${package_path}" "${stage_dir}"; then
fail "normalized zip extraction failed for ${package_path}"
echo "ERROR: normalized zip extraction failed for ${package_path}" >&2
return 1
fi
local manifest_path host evtx_root archive_pkg_dir archive_extract_dir status report_dir
@@ -453,7 +456,47 @@ process_one_package() {
if [ -n "${report_dir}" ]; then
echo "Report directory: ${report_dir}"
fi
[ "${status}" = "ok" ] || fail "Package workflow ended with status=${status}; archived for inspection"
if [ "${status}" != "ok" ]; then
echo "ERROR: Package workflow ended with status=${status}; archived for inspection" >&2
return 1
fi
}
quarantine_incoming_package() {
local package_path="$1"
local reason="$2"
local ts package_name package_base safe_base target_dir stage_dir sha256
ts="$(date -u +%Y%m%dT%H%M%SZ)"
package_name="$(basename "${package_path}")"
package_base="${package_name%.zip}"
safe_base="$(sanitize "${package_name}")"
[ -n "${safe_base}" ] || safe_base="package.zip"
target_dir="${HAYA_QUARANTINE_DIR}/${ts}_${safe_base}"
mkdir -p "${target_dir}"
sha256=""
if [ -f "${package_path}" ] && command -v sha256sum >/dev/null 2>&1; then
sha256="$(sha256sum "${package_path}" | awk '{print $1}')"
fi
for candidate in "${package_path}" "${package_path}.sha256" "${package_path}.host"; do
if [ -e "${candidate}" ]; then
mv "${candidate}" "${target_dir}/"
fi
done
stage_dir="${HAYA_STAGING_DIR}/${package_base}"
if [ -d "${stage_dir}" ]; then
mv "${stage_dir}" "${target_dir}/staging-partial"
fi
cat >"${target_dir}/reason.json" <<EOF
{
"quarantined_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
"source": "aw-hayabusa process-inbox",
"original_path": "${package_path}",
"sha256": "${sha256}",
"reason": "${reason}",
"operator_action": "inspect source package, re-export EVTX archive if needed, then replay by moving a fixed package back to incoming or drop"
}
EOF
echo "Quarantined failed incoming package: ${target_dir}" >&2
}
process_inbox() {
@@ -482,15 +525,26 @@ process_inbox() {
esac
ensure_layout
local count=0 pkg
local count=0 failed=0 pkg
while IFS= read -r pkg; do
process_one_package "${pkg}" "${mode}"
count=$((count + 1))
if process_one_package "${pkg}" "${mode}"; then
count=$((count + 1))
else
failed=$((failed + 1))
if [ -f "${pkg}" ]; then
quarantine_incoming_package "${pkg}" "process_one_package failed"
else
echo "Package failed after archive/move, see archive intake manifest for details: ${pkg}" >&2
fi
fi
if [ "${limit}" -gt 0 ] && [ "${count}" -ge "${limit}" ]; then
break
fi
done < <(find "${HAYA_INCOMING_DIR}" -maxdepth 1 -type f -name '*.zip' | sort)
[ "${count}" -gt 0 ] || echo "No packages in ${HAYA_INCOMING_DIR}"
if [ "${failed}" -gt 0 ]; then
echo "process-inbox completed with quarantined_or_archived_failures=${failed}" >&2
fi
}
main() {