Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions anyscan-worker-only.service
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,20 @@ ExecStart=/opt/agentd/bin/agentd daemon
Restart=always
RestartSec=5
NoNewPrivileges=true
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW
# CAP_BPF is required for the AF_XDP I/O path the scanner gains in
# Phase 2 of plans/2026-04-27-portscan-afxdp-plan-v1.md (§4.4): opening an
# XSK socket and attaching libxdp's default redirect program both go
# through the bpf() syscall. The scanner only opens an XSK when invoked
# with --io-engine=af_xdp; AF_PACKET (the default) does not need CAP_BPF,
# so granting it here is purely a runtime gate that lets workers opt in
# via ANYSCAN_SCANNER_IO_ENGINE without a unit file refresh. CAP_BPF is
# scoped narrowly: it does not imply CAP_SYS_ADMIN or kernel-module load
# rights. Older (<5.8) kernels collapse it into CAP_SYS_ADMIN and the
# capability bits we set here are simply ignored — the install-time
# probe in install-worker-bundle.sh refuses to enable af_xdp on those
# hosts so the unit-file grant has no effect there either.
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CAP_BPF
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_BPF
PrivateTmp=true
PrivateDevices=true
ProtectSystem=strict
Expand Down
16 changes: 14 additions & 2 deletions anyscan-worker.service
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,20 @@ ExecStart=/opt/anyscan/bin/anyscan-worker daemon
Restart=always
RestartSec=5
NoNewPrivileges=true
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW
# CAP_BPF is required for the AF_XDP I/O path the scanner gains in
# Phase 2 of plans/2026-04-27-portscan-afxdp-plan-v1.md (§4.4): opening an
# XSK socket and attaching libxdp's default redirect program both go
# through the bpf() syscall. The scanner only opens an XSK when invoked
# with --io-engine=af_xdp; AF_PACKET (the default) does not need CAP_BPF,
# so granting it here is purely a runtime gate that lets workers opt in
# via ANYSCAN_SCANNER_IO_ENGINE without a unit file refresh. CAP_BPF is
# scoped narrowly: it does not imply CAP_SYS_ADMIN or kernel-module load
# rights. Older (<5.8) kernels collapse it into CAP_SYS_ADMIN and the
# capability bits we set here are simply ignored — the install-time
# probe in install-worker-bundle.sh refuses to enable af_xdp on those
# hosts so the unit-file grant has no effect there either.
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CAP_BPF
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_BPF
PrivateTmp=true
PrivateDevices=true
ProtectSystem=strict
Expand Down
49 changes: 49 additions & 0 deletions install-external-deps.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,53 @@ print_banner() {
printf '═══════════════════════════════════════════════════════════\n'
}

# Install build-time dependencies for the AF_XDP I/O path the scanner gains
# in Phase 2 of plans/2026-04-27-portscan-afxdp-plan-v1.md (§4.1). The fork
# Makefile only pulls these in when invoked with `make USE_AF_XDP=1`; with
# the default `make` they are unused, so we make the install best-effort:
# run only when apt-get is available AND we have permission to install
# packages, skip otherwise with a one-line note. The scanner build still
# succeeds without these packages — `make USE_AF_XDP=1` would fail
# loudly later, which is the right escalation.
#
# Set ANYSCAN_INSTALL_AFXDP_DEPS=false to suppress this block (e.g. on
# AMIs where the operator pre-pinned a different libxdp version).
install_afxdp_build_deps() {
if [ "${ANYSCAN_INSTALL_AFXDP_DEPS:-true}" != "true" ]; then
return 0
fi
if ! command -v apt-get >/dev/null 2>&1; then
printf '[*] Skipping AF_XDP build deps: apt-get not on PATH (non-Debian host).\n'
return 0
fi
local apt_cmd=()
if [ "$(id -u 2>/dev/null || echo 1)" = "0" ]; then
apt_cmd=(apt-get)
elif command -v sudo >/dev/null 2>&1; then
apt_cmd=(sudo -n apt-get)
else
printf '[*] Skipping AF_XDP build deps: not root and sudo is not available.\n'
printf ' Install manually if you plan to build the scanner with USE_AF_XDP=1:\n'
printf ' sudo apt-get install -y libxdp-dev libbpf-dev libelf-dev\n'
return 0
fi
# Probe sudo non-interactively; if it would prompt, bail rather than
# block the script in CI.
if [ "${apt_cmd[0]}" = "sudo" ] && ! sudo -n true >/dev/null 2>&1; then
printf '[*] Skipping AF_XDP build deps: sudo would prompt for a password.\n'
printf ' Install manually if you plan to build the scanner with USE_AF_XDP=1:\n'
printf ' sudo apt-get install -y libxdp-dev libbpf-dev libelf-dev\n'
return 0
fi
printf '[*] Installing AF_XDP build deps (libxdp-dev libbpf-dev libelf-dev)...\n'
if ! "${apt_cmd[@]}" install -y --no-install-recommends \
libxdp-dev libbpf-dev libelf-dev >/dev/null; then
printf '[!] apt-get install of AF_XDP build deps failed; the scanner will still build with default `make`.\n' >&2
printf ' Re-run with USE_AF_XDP=1 only after libxdp-dev / libbpf-dev / libelf-dev are present.\n' >&2
return 0
fi
}

upsert_env_value() {
local key="$1"
local value="$2"
Expand Down Expand Up @@ -56,6 +103,8 @@ if ! command -v git >/dev/null 2>&1; then
exit 1
fi

install_afxdp_build_deps

if [ -d "$VULNSCANNER_REPO_DIR/.git" ]; then
printf '[*] Updating external repository in %s...\n' "$VULNSCANNER_REPO_DIR"
git -C "$VULNSCANNER_REPO_DIR" fetch --tags --prune
Expand Down
56 changes: 56 additions & 0 deletions install-worker-bundle.sh
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,61 @@ apply_host_resource_defaults() {
fi
}

probe_afxdp_runtime_available() {
# Phase 2 PR C of plans/2026-04-27-portscan-afxdp-plan-v1.md §4.3.
# The scanner can be invoked with --io-engine=af_xdp only when (a) the
# kernel is recent enough that XDP_USE_NEED_WAKEUP behaves correctly
# (≥5.10, plan §4.3) and (b) libxdp.so is loadable on the host (the
# binary is dynamically linked against it when built with
# USE_AF_XDP=1; if the runtime libs are absent the scanner crashes
# at startup with a dlopen error). Both checks are cheap, so do them
# at install time and write ANYSCAN_AF_XDP_AVAILABLE so downstream
# code (the adapter, future tooling) does not have to redo the
# probe per-scan. The variable defaults to "false" — operators can
# override to "true" by hand if they know the bundle's bin/scanner
# was built with USE_AF_XDP=1 and they have the libs from a path not
# visible to ldconfig (e.g. LD_LIBRARY_PATH).
local kernel_release kernel_major kernel_minor
kernel_release="$(uname -r 2>/dev/null || true)"
if [ -z "$kernel_release" ]; then
printf 'false'
return 0
fi
kernel_major="${kernel_release%%.*}"
local rest="${kernel_release#*.}"
kernel_minor="${rest%%.*}"
case "$kernel_major" in ''|*[!0-9]*) printf 'false'; return 0 ;; esac
case "$kernel_minor" in ''|*[!0-9]*) kernel_minor=0 ;; esac
if [ "$kernel_major" -lt 5 ] || { [ "$kernel_major" -eq 5 ] && [ "$kernel_minor" -lt 10 ]; }; then
printf 'false'
return 0
fi
if ! command_exists ldconfig; then
printf 'false'
return 0
fi
if ! ldconfig -p 2>/dev/null | grep -q '\<libxdp\.so'; then
printf 'false'
return 0
fi
printf 'true'
}

apply_afxdp_availability() {
# Always write the flag (true OR false) so the value is explicit in
# /etc/agentd/runtime.env and a partial upgrade can't leave a stale
# "true" in place after the kernel was downgraded or libxdp was
# uninstalled. This mirrors the AGENT_REMOTE_UPDATE_* pattern above.
local afxdp_available
afxdp_available="$(probe_afxdp_runtime_available)"
upsert_env_value "ANYSCAN_AF_XDP_AVAILABLE" "$afxdp_available" "$RUNTIME_ENV_FILE"
if [ "$afxdp_available" = "true" ]; then
printf '[*] AF_XDP runtime probe passed (kernel + libxdp.so present); ANYSCAN_AF_XDP_AVAILABLE=true.\n'
else
printf '[*] AF_XDP runtime probe failed (kernel <5.10 or libxdp.so missing); ANYSCAN_AF_XDP_AVAILABLE=false.\n'
fi
}

apply_scanner_host_tunings() {
if [ ! -x "$TUNE_SCANNER_HOST_HELPER_DEST_FILE" ]; then
return 0
Expand Down Expand Up @@ -727,6 +782,7 @@ main() {
fi

apply_host_resource_defaults "$cpu_threads"
apply_afxdp_availability
apply_scanner_host_tunings

if [ "$existing_install" = "true" ]; then
Expand Down