Conversation
On x86_64, the syscall is named "newfstatat" while on arm64 it's "fstatat". The previous code always used "fstatat" which caused seccomp-bpf filter assembly to fail on x86_64 with: "failed to assemble policy: found unknown syscalls for arch x86_64: fstatat" This change adds a runtime check for GOARCH to use the correct syscall name. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add containerization support using Google's distroless base image for
minimal attack surface and small image size (~24MB).
Files added in contrib/docker/:
- Dockerfile: Multi-stage build with golang builder and distroless runtime
- docker-compose.yml: Simple deployment configuration
- gosh.container.yml: Template configuration for containerized deployment
- config/: Example configuration directory with gosh.yml and index.html
Key design decisions:
1. Base Image: gcr.io/distroless/static-debian12
- Minimal attack surface (no shell, no package manager)
- Small image size (~2MB base + ~6MB gosh binary)
- Follows security best practices used by Kubernetes, Knative, etc.
2. Privilege Dropping: Leverages gosh's built-in security model
- Container starts as root (required for chroot/setuid syscalls)
- gosh drops privileges to UID 65532 (nonroot) after initialization
- Seccomp-BPF filters are applied by gosh itself
- Custom /etc/passwd and /etc/group for user.Lookup() compatibility
3. Configuration:
- Mount /config directory with gosh.yml and optional static files
- Mount /data volume for persistent storage
- All paths in container config point to /config/* and /data/*
Usage (from contrib/docker/):
docker compose up -d
Or manually (from repo root):
docker build -f contrib/docker/Dockerfile -t gosh:distroless .
docker run -d -p 8080:8080 \
-v ./contrib/docker/config:/config:ro \
-v gosh-data:/data \
gosh:distroless
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
oxzi
left a comment
There was a problem hiding this comment.
First, thanks a lot for using gosh and this PR. Please excuse the delay.
In general, I would like to have a Dockerfile in here, but there are a few things needed to be addressed first. Please take a look at my comments, added after a first vibe check of this PR.
| } else { | ||
| seccompFilter = append(seccompFilter, "newfstatat") |
There was a problem hiding this comment.
This is not required, since newfstatat is already part of @system-services. Already described when introducing this change in c55f4fa.
https://github.com/oxzi/syscallset-go/blob/v0.1.7/syscalls.go#L1133-L1333
Furthermore, this binary logic assumes that newfstatat is available on every architecture that is not arm64. Unsure about that.
| // fstatat syscall name differs per architecture | ||
| if runtime.GOARCH == "arm64" { | ||
| seccompFilter = append(seccompFilter, "fstatat") |
There was a problem hiding this comment.
Meh. Seems like I don't know my own library. I assumed that syscallset-go ignore unknown system calls, but it only does so when passing a syscall set.
I have changed this with the just released - and already bumped here - version 0.1.7.
Thus, no further changes in the Go code are required from this PR. Just rebase :)
There was a problem hiding this comment.
I am not a huge fan of having duplicated configuration files. Changes to the main gosh.yml will be forgotten and not being reapplied here.
Thus, how about copying it into the container, do the necessary changes via sed or yq within the container build stage and still allow users to overlay/overwrite it?
There was a problem hiding this comment.
Quite same as https://github.com/oxzi/gosh/pull/73/changes#r2738584441. Is there even a change compared to ./index.html?
| @@ -0,0 +1,45 @@ | |||
| # Build stage | |||
| FROM golang:1.23-bookworm AS build | |||
There was a problem hiding this comment.
Please don't use an LLM (w/o Internet access) to pin "latest" versions.
Especially in this case, these versions are dangerously outdated, as Go only supports the two last major versions, being 1.24 and 1.25 at the time of writing. Since Go 1.23's EOL, there were multiple security fixes in 1.24 and 1.25, many of those might also affect 1.23.
Furthermore, Debian stable is trixie now.
| EXPOSE 8080 | ||
|
|
||
| ENTRYPOINT ["/gosh"] | ||
| CMD ["-config", "/config/gosh.yml"] |
There was a problem hiding this comment.
The /config directory is not mentioned as a VOLUME in the Dockerfile, but only referenced through the docker-compose.yml file.
There was a problem hiding this comment.
What is this file for? Is it even being referenced?
Summary
This PR adds Docker containerization support using Google's distroless base image, providing a minimal and secure deployment option for gosh under linux
Changes
Bug fix: Architecture-specific syscall name for
fstatat/newfstatatfstatatis only valid on arm64runtime.GOARCHto select the correct syscall nameDocker support:
Dockerfile: Multi-stage build with distroless runtimedocker-compose.yml: Simple deployment configurationgosh.container.yml: Template configurationconfig/: Example configuration directoryDesign Decisions
Base Image:
gcr.io/distroless/static-debian12Privilege Model
The container leverages gosh's built-in privilege separation:
chroot()/setuid()syscalls)/etc/passwdand/etc/groupadded foruser.Lookup()compatibilityConfiguration
Usage
Pre-built Image
A pre-built image is available on Docker Hub:
gorja/gosh:0.6.0Test plan
🤖 Generated with Claude Code