Skip to content

Latest commit

 

History

History
147 lines (114 loc) · 4.81 KB

README.md

File metadata and controls

147 lines (114 loc) · 4.81 KB

[📂Profiles]

gomodjail: jail for Go modules

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.

Requirements

Runtime dependencies:

  • Linux (4.8 or later) or macOS
  • x86_64 (aka "amd64") or aarch64 ("arm64")

Build dependencies:

Install

make
sudo make install

Makefile variables:

  • PREFIX: installation prefix (default: /usr/local)

Example

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

More examples

examples/profiles has several example profiles:

  • docker.mod: for docker (not dockerd)
  • ...

Caveats

  • Not applicable to a Go binary built by non-trustworthy thirdparty, as the symbol information might be faked.
  • Not applicable to a Go binary built with -ldflags="-s" (disable symbol table)
  • Not applicable to a Go module that use:
  • No isolation of file descriptors across modules. A confined module can still read/write an existing file descriptor, although it cannot open a new file descriptor.
  • The target binary file must not be replaced during execution.
  • The gomodjail:confined policy is not well defined and still subject to change.
  • This is not a panacea; there can be other loopholes too.

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
  • Not applicable to a Go module that use:

Advanced topics

Advanced usage

See gomodjail run --help

$ gomodjail run --help
Run a Go program with confinement

Usage:
  gomodjail run COMMAND...

Examples:
TBD

Flags:
      --go-mod gomodjail:confined   go.mod file with comment lines like gomodjail:confined
  -h, --help                        help for run
      --no-policy                   Allow running without any policy (useful only for debugging)
      --policy stringToString       e.g., example.com/module=confined (default [])

Global Flags:
      --debug   debug mode [$DEBUG]

How it works

Linux:

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).

Future works

  • Automatically detect non-applicable modules (explained in Caveats).
  • Support embedding gomodjail in a target program
  • Apply landlock in addition to seccomp. Depends on SECCOMP_IOCTL_NOTIF_ADDFD.
  • Modify the source code of the Go runtime, so as to remove necessity of using seccomp (Linux) and DYLD_INSERT_LIBRARIES (macOS).

Additional documents