Skip to content

Harden nut-server image: remove default guest credentials, disable local drive access, run as non-root - #3

Open
devin-ai-integration[bot] wants to merge 1 commit into
mainfrom
devin/1786522122-harden-nut-server
Open

Harden nut-server image: remove default guest credentials, disable local drive access, run as non-root#3
devin-ai-integration[bot] wants to merge 1 commit into
mainfrom
devin/1786522122-harden-nut-server

Conversation

@devin-ai-integration

Copy link
Copy Markdown

Summary

Security review of the image. The repo itself contains no application code, so all findings are about what the Dockerfile bakes into the published image from blawar/nut v3.3.

Critical, verified on the current main image: upstream ships conf/users.conf containing guest|guest, and the image kept it. Nut's HTTP server (0.0.0.0:9000, HTTP basic auth) therefore accepts guest:guest out of the box. The bundled config also kept "enableLocalDriveAccess": 1, which in Server/Controller/Api.py disables both the .. check and the "path must be inside a configured path" check and adds root (i.e. /) to the browsable drive list — so any client can read arbitrary files from the container, which runs as root.

# main image, no credentials configured by the operator
$ curl -u guest:guest localhost:9000/api/directoryList
{"dirs": [{"name": "titles"}, {"name": "root"}], "files": []}   # <- "root" == container filesystem

# this branch
$ curl localhost:9000/api/directoryList                 -> 401
$ curl -u guest:guest localhost:9000/api/directoryList  -> 401
$ curl -u nut:<generated> localhost:9000/api/directoryList
{"dirs": [{"name": "titles"}], "files": []}

Fixes

  • Delete the shipped users.conf; a new docker-entrypoint.sh writes one on first start from NUT_USERNAME/NUT_PASSWORD, or generates a random 24-char password and prints it once to the logs. It also warns if a mounted users.conf still holds guest|guest.
  • "enableLocalDriveAccess": 0 in the baked config.
  • The entrypoint restores conf/nut.conf from /opt/nut-defaults/nut.conf when it is missing. Mounting an empty host dir on the conf volume used to hide the baked config entirely, silently falling back to Nut's defaults (scan ., local drive access on).
  • Run the server as uid 1000 via gosu after the entrypoint fixes ownership of the mounted volumes, so existing root-owned host mounts keep working. Install paths are unchanged.
  • Verify the release archive against a pinned NUT_SHA256 instead of trusting an unauthenticated wget.
  • Drop the build toolchain (gcc, *-dev) and apt lists from the final layer, and remove ShopN.pem — an upstream-bundled Nintendo shop client certificate with a private key, only referenced as a config default and unused by the server.
  • Bump requests (>=2.32.3), Pillow (>=10.3.0) and urllib3 (>=1.26.19) past known CVEs; the previous ~= pins resolved to requests 2.25.1 and Pillow 8.0.1.

Not fixed / notes

  • No SQL, no CORS handling and no debug endpoints exist in this stack; there is no .env or credential material in the repo.
  • Auth is plain-HTTP basic auth with plaintext passwords compared non-constant-time in nut/Users.py (upstream). README now tells operators to keep it on a trusted network or behind a TLS proxy.
  • Upstream Flask~=1.1.2 (plus the markupsafe==2.0.1 pin needed to make it install) is only imported by nut/blockchain.py, which the server never loads — left alone to avoid churn, but it could be dropped from requirements.txt.

Verified by building both images and exercising the endpoints above; the hardened container starts, serves /titles, and reports its generated credentials in docker logs.

Link to Devin session: https://app.devin.ai/sessions/f51efaa8ae66475ab14521388f4b8ee7
Requested by: @eskwisit

…-root

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@eskwisit eskwisit self-assigned this Aug 12, 2026
@devin-ai-integration

Copy link
Copy Markdown
Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant