Repository navigation
Conversation
Nmap, its NSE scripts and Naabu parse responses from the networks they scan, and ran as UID 0 with read access to the database and both encryption keys. They now start as UID and GID 65532 with no supplementary groups, keeping only NET_RAW (and NET_ADMIN when the container grants it) as ambient capabilities. They read their target list and write their XML through inherited file descriptors, so they can neither list the UID 0 data directory nor reach config.yaml. - scanner.sandbox: auto (default) confines scanner processes when the container allows it and otherwise runs them unconfined with a warning; required refuses to start the daemon or a scan without the sandbox; off keeps the previous behaviour. - At startup EdgeWatch checks for SETUID, SETGID and KILL and runs a short confined probe. The bundled compose.yaml adds the three capabilities; an older compose.yaml keeps scanning unconfined. - Nmap is started with --privileged so it uses its ambient NET_RAW; scan results and recorded command fingerprints are unchanged. - edgewatch health and the Overview report the sandbox state, and warn when scanner processes run unconfined as UID 0. - The image names UID 65532 edgewatch-scanner. - CI and the release smoke stage run real Nmap SYN, UDP and Naabu scans of local listeners through EdgeWatch with the sandbox enforced and off, require identical results, and check the running Nmap's identity, cancellation and data-directory access.
Deploying edgewatch with
|
| Latest commit: |
952fd1d
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://aaf7ff77.edgewatch-cpd.pages.dev |
| Branch Preview URL: | https://feat-scanner-sandbox.edgewatch-cpd.pages.dev |
Follow the store's platform pattern: the common file defines Detect, Confine and the capability names with hooks that only Linux installs, so no file is excluded by build constraints on the platform that measures coverage.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Nmap, its NSE scripts and Naabu parse responses from the networks they scan, and so far they ran as UID 0 inside the container, with read access to the SQLite database and both encryption keys. This PR runs them in a scanner sandbox:
edgewatch-scanner), with no supplementary groups.NET_RAW, plusNET_ADMINwhen the container grants it, held as ambient capabilities.config.yaml.The daemon itself still runs as UID 0. The blocker the docs gave for going non-root, Docker stripping
NET_RAWfrom a non-root container process, doesn't apply here: the daemon switches the identity for each child process. Bind-mount ownership and rollback are therefore unchanged.How it works
internal/sandbox:Detect(mode)checks that the process is euid 0 and holdsSETUID,SETGIDandKILL. WithoutKILL, UID 0 cannot signal another UID once capabilities are dropped, so cancellation and timeouts would break.edgewatch versionas UID 65532 with the ambient capabilities) before enforcing.Confine(cmd)sets the credentials and ambient capabilities and keeps the pty'sSetsid/Setctty.InheritFileshares a file with only the needed other-permission bits: 0604 to read, 0602 to write.--privileged, because Nmap otherwise assumes a non-root process cannot send raw packets. The flag goes only into the executed argv; the recorded command fingerprint is computed without it./dev/fd/N; Naabu reads its targets through/dev/fd/N.CAP_NET_RAW(verified at the pinned v2.6.1 commit).scanner.sandboxauto(default): confine when possible, otherwise run unconfined with a warning;required: refuse to start the daemon orscanbefore the database is opened;off: previous behaviour.edgewatch healthgainsscanner_sandbox(state,process_uid,capabilities,reason), with a warning when scanners run unconfined as UID 0;/api/v1/statusreports it to administrators and operators;compose.yamladdsSETUID,SETGIDandKILL;verify-compose-policy.shnow requires that exact set (plusNET_ADMINfor the SYN override), and its tests are updated;edgewatch-scanneruser and group (65532). It has no setuid or file-capability binaries (checked).Proof
scripts/verify-scanner-sandbox.sh IMAGEis new and runs in the CI container job and in the release smoke stage. It starts real daemons with real scanners against local listeners:Uid 65532,Gid 65532, no groups, andCapEff 0000000000002000(NET_RAWonly)./var/lib/edgewatchor read/etc/edgewatch/config.yaml.enforced 65532 NET_RAW, thenNET_RAW,NET_ADMINwith the SYN override, thendisabledwhen off.Naabu SYN results aren't compared, because SYN discovery of loopback drops ports at any rate, even unconfined. The SYN sandbox is covered by its capability report and by a manual run: a sandboxed Naabu reported "Running SYN scan with CAP_NET_RAW privileges" and found the open ports.
Compatibility
compose.yamlfrom an earlier release lacks the three capabilities, soautokeeps scanning unconfined as UID 0 and warns. The docs explain how to add them (container hardening, updates and troubleshooting guides).scanner.sandboxconfig key,scanner_sandboxin the health and status documents, and theedgewatch-scanneruser in the image.Not covered (documented)
Validation
gofmt,go vet ./..., andGOOS=darwin go build ./.... As ininternal/store, only Linux installs the sandbox hooks; other platforms report it unavailable.go test -race -timeout=25m ./...: every package passed.internal/webuifailed once because I rebuilt the frontend while the suite was compiling; it passed on rerun.internal/sandboxpackage has 97% coverage.npm run lint,npm run build,npm run test:coverage: 45 files, 500 tests, gates passed.docker compose config --quietfor both renders;verify-compose-policy.sh base|syn;node --test scripts/verify-compose-policy.test.mjs(100 tests);node --test scripts/release-workflow.test.mjs.npm --prefix docs run build: 1296 links verified../scripts/verify-scanner-sandbox.shagainst a locally built image: passed twice, about 1 minute per run.