gomodjail imposes syscall restrictions on a specific set of Go modules, so as to mitigate their potential vulnerabilities and supply chain attack vectors.
In other words, gomodjail is a "container" (as in Docker containers) for Go modules.
gomodjail can be applied just in the following two steps:
Step 1: add gomodjail:confined comment to go.mod:
require (
example.com/module v1.0.0 // gomodjail:confined
)
Step2: run the program with gomodjail run --go-mod=go.mod:
gomodjail run --go-mod=FILE -- PROG [ARGS]...
[!IMPORTANT]
See Caveats.
Runtime dependencies: - Linux (4.8 or later) or macOS - x86_64 (aka "amd64") or aarch64 ("arm64")
Build dependencies: - Go
make
sudo make install
Makefile variables:
- PREFIX: installation prefix (default: /usr/local)
An example program is located in ./examples/victim:
cd ./examples/victim
go build
./victim
Confirm the "malicious" vi screen:
*** ARBITRARY SHELL CODE EXECUTION ***
This 'vi' command was executed by the 'github.com/AkihiroSuda/gomodjail/examples/poisoned' module.
This example is harmless, of course, but suppose that this was a malicious code.
Type ':q!' to leave this screen.
Run the program again with gomodjail run --go-mod=go.mod, and confirm that the execution of the "malicious" vi command is blocked.
gomodjail run --go-mod=go.mod -- ./victim
level=WARN msg=***Blocked*** syscall=pidfd_open module=github.com/AkihiroSuda/gomodjail/examples/poisoned
examples/profiles has several example profiles:
- docker.mod: for docker (not dockerd)
- ...
unsafe, reflect, plugin, etc. See Static analysis.gomodjail:confined policy is not well defined and still subject to change.macOS:
- The protection can be arbitraliry disabled by unsetting an environment variable DYLD_INSERT_LIBRARIES.
- Only works with the following versions of Go:
- 1.22
- 1.23
- 1.24 (excluding 1.24.0-1.24.5)
- 1.25
- 1.26
- Not applicable to a Go module that use:
- syscall.Syscall, syscall.RawSyscall, etc.
macOS on Intel:
- Not applicable to a Go binary built with -ldflags="-s" (disable symbol table)
To create a self-extract archive of gomodjail with a target program, run gomodjail pack --go-mod=go.mod PROGRAM.
The self-extract archive is created as <PROGRAM>.gomodjail.
The gomodjail analyze command can analyze if a module imports "unsafe" codes that cannot be protected with gomodjail, such as:
- unsafe
- reflect
- plugin
- go:linkname
- C
- Assembly
$ gomodjail analyze ./...
/Users/suda/gopath/pkg/mod/golang.org/x/sys@v0.39.0/cpu/runtime_auxv_go121.go:10:2: unsafe or cgo-related package imported: "unsafe"
/Users/suda/gopath/pkg/mod/golang.org/x/sys@v0.39.0/cpu/runtime_auxv_go121.go:13:1: use of //go:linkname directive detected
/Users/suda/gopath/pkg/mod/golang.org/x/sys@v0.39.0/execabs/execabs.go:21:2: unsafe or cgo-related package imported: "reflect"
/Users/suda/gopath/pkg/mod/golang.org/x/sys@v0.39.0/execabs/execabs.go:22:2: unsafe or cgo-related package imported: "unsafe"
[...]
Linux:
- SECCOMP_RET_TRACE is used for conditionally
allowing trusted Go modules to execute the syscall.
SECCOMP_RET_USER_NOTIF is not used because it cannot access all the CPU registers,
due to the lack of struct pt_regs in struct seccomp_data.
- Stack unwinding is used for analyzing the call stack to determine the Go module.
macOS:
- DYLD_INSERT_LIBRARIES is used to hook libSystem (libc) calls.
- In addition to the frame pointer (AArch64 register X29), struct g in the TLS and g->m.libcallsp are parsed to analyze the CGO call stack.
This analysis is not robust and only works with specific versions of Go. (See Caveats).
SECCOMP_IOCTL_NOTIF_ADDFD.seccomp (Linux) and DYLD_INSERT_LIBRARIES (macOS).docs/syntax.md: syntaxexamples/profiles/README.md: profiles$ claude mcp add gomodjail \
-- python -m otcore.mcp_server <graph>