runc Documentation

CLI tool for spawning and running containers according to the OCI specification

13,498 stars Go Go 32 files ยท ~30,975 tokens #containers#docker#oci
RAW Doc

Docker & Self-Hosted Deployment Verified OCI

Deploy runc on any server, homelab, or cloud container platform.

1-Click Terminal Command
bash (docker run)
docker run -d --name runc --restart always opencontainers/runc:latest

Technical Documentation: opencontainers/runc

โ„น๏ธ Provenance: Multi-Tier Ingestion ยท CodeWiki Reference

Overview (`README.md`)

runc




Introduction

runc is a CLI tool for spawning and running containers on Linux according to the OCI specification.

Releases

You can find official releases of runc on the release page.

All releases are signed by one of the keys listed in the runc.keyring file in the root of this repository.

Security

The reporting process and disclosure communications are outlined here.

Security Audit

A third party security audit was performed by Cure53, you can see the full report here.

Building

runc only supports Linux. See the header of go.mod for the minimally required Go version.

Pre-Requisites

Utilities and Libraries

In addition to Go, building runc requires multiple utilities and libraries to be installed on your system.

On Ubuntu/Debian, you can install the required dependencies with:

bash
apt update && apt install -y make gcc linux-libc-dev libseccomp-dev pkg-config git

On CentOS/Fedora, you can install the required dependencies with:

bash
yum install -y make gcc kernel-headers libseccomp-devel pkg-config git

On Alpine Linux, you can install the required dependencies with:

bash
apk --update add bash make gcc libseccomp-dev musl-dev linux-headers git

The following dependencies are optional:

  • libseccomp - only required if you enable seccomp support; to disable, see Build Tags.
  • libpathrs - only required if you enable libpathrs support; to disable, see Build Tags.
    For notes on installing libpathrs, see the next section.

libpathrs

libpathrs is a Rust library runc can optionally use for path safety. As
mentioned in the build tag section, its use is controlled with
the libpathrs build tag. runc currently requires at least libpathrs 0.2.5
in order to function properly.

At time of writing, very few distributions have libpathrs packages and so it is
usually necessary to build and install it locally. For detailed installation
instructions, see the upstream documentation, but for
development builds the following instructions should be sufficient:

libpathrs requires Rust 1.63+ (which is available on almost any distribution,
including Debian oldstable and enterprise distributions like RHEL or SLES).
Assuming you already have cargo installed (as well as other libpathrs
dependencies like clang and lld), the following steps are all that are
really necessary to install libpathrs:

sh
LIBPATHRS_VERSION=0.2.5
curl -o - -sSL https://github.com/cyphar/libpathrs/releases/download/v${LIBPATHRS_VERSION}/libpathrs-${LIBPATHRS_VERSION}.tar.xz | tar xvfJ -
cd libpathrs-${LIBPATHRS_VERSION}/
make release
sudo ./install.sh --prefix=/usr/local
sudo ldconfig

As part of our CI, we make use of a custom installation script for
libpathrs
which may be useful as a reference for
folks with more complicated needs. With script/build-libpathrs.sh the
installation of libpathrs becomes as simple as:

sh
sudo ./script/build-libpathrs.sh "$LIBPATHRS_VERSION" /usr/local
sudo ldconfig

However, please note that this installation script is completely
unsupported
and is not really intended for general use (it includes some
workarounds for issues in our CI which will no longer be necessary once
libpathrs has distribution packages we can use).

Build

bash
# create a 'github.com/opencontainers' in your GOPATH/src
cd github.com/opencontainers
git clone https://github.com/opencontainers/runc
cd runc

make
sudo make install

You can also use go get to install to your GOPATH, assuming that you have a github.com parent folder already created under src:

bash
go get github.com/opencontainers/runc
cd $GOPATH/src/github.com/opencontainers/runc
make
sudo make install

runc will be installed to /usr/local/sbin/runc on your system.

Version string customization

You can see the runc version by running runc --version. You can append a custom string to the
version using the EXTRA_VERSION make variable when building, e.g.:

bash
make EXTRA_VERSION="+build-1"

Bear in mind to include some separator for readability.

Build Tags

runc supports optional build tags for compiling support of various features,
with some of them enabled by default in the top-level Makefile.

The following build tags are currently recognized:

Build Tag Feature Set by Default Dependencies
seccomp Syscall filtering using libseccomp. yes libseccomp
libpathrs Use libpathrs for path safety. yes libpathrs
runc_nocriu Disables runc checkpoint/restore. no criu

To add or remove build tags from the default set, use the RUNC_BUILDTAGS
make or shell variable. Tags prefixed with - are removed from the default set;
others are added. For example:

bash
# Add runc_nocriu and remove seccomp tag.
make RUNC_BUILDTAGS="runc_nocriu -seccomp"

The following build tags were used earlier, but are now obsoleted:

  • runc_nodmz (since runc v1.2.1 runc dmz binary is dropped)
  • nokmem (since runc v1.0.0-rc94 kernel memory settings are ignored)
  • apparmor (since runc v1.0.0-rc93 the feature is always enabled)
  • selinux (since runc v1.0.0-rc93 the feature is always enabled)

Running the test suite

runc currently supports running its test suite via Docker.
To run the suite just type make test.

bash
make test

There are additional make targets for running the tests outside of a container but this is not recommended as the tests are written with the expectation that they can write and remove anywhere.

You can run a specific test case by setting the TESTFLAGS variable.

bash
# make test TESTFLAGS="-run=SomeTestFunction"

You can run a specific integration test by setting the TESTPATH variable.

bash
# make test TESTPATH="/checkpoint.bats"

You can run a specific rootless integration test by setting the ROOTLESS_TESTPATH variable.

bash
# make test ROOTLESS_TESTPATH="/checkpoint.bats"

You can run a test using your container engine's flags by setting CONTAINER_ENGINE_BUILD_FLAGS and CONTAINER_ENGINE_RUN_FLAGS variables.

bash
# make test CONTAINER_ENGINE_BUILD_FLAGS="--build-arg http_proxy=http://yourproxy/" CONTAINER_ENGINE_RUN_FLAGS="-e http_proxy=http://yourproxy/"

Go Dependencies Management

runc uses Go Modules for dependencies management.
Please refer to Go Modules for how to add or update
new dependencies.

text
# Update vendored dependencies
make vendor
# Verify all dependencies
make verify-dependencies

Using runc

Please note that runc is a low level tool not designed with an end user
in mind. It is mostly employed by other higher level container software.

Therefore, unless there is some specific use case that prevents the use
of tools like Docker or Podman, it is not recommended to use runc directly.

If you still want to use runc, here's how.

Creating an OCI Bundle

In order to use runc you must have your container in the format of an OCI bundle.
If you have Docker installed you can use its export method to acquire a root filesystem from an existing Docker container.

bash
# create the top most bundle directory
mkdir /mycontainer
cd /mycontainer

# create the rootfs directory
mkdir rootfs

# export busybox via Docker into the rootfs directory
docker export $(docker create busybox) | tar -C rootfs -xvf -

After a root filesystem is populated you just generate a spec in the format of a config.json file inside your bundle.
runc provides a spec command to generate a base template spec that you are then able to edit.
To find features and documentation for fields in the spec please refer to the specs repository.

bash
runc spec

Running Containers

Assuming you have an OCI bundle from the previous step you can execute the container in two different ways.

The first way is to use the convenience command run that will handle creating, starting, and deleting the container after it exits.

bash
# run as root
cd /mycontainer
runc run mycontainerid

If you used the unmodified runc spec template this should give you a sh session inside the container.

The second way to start a container is using the specs lifecycle operations.
This gives you more power over how the container is created and managed while it is running.
This will also launch the container in the background so you will have to edit
the config.json to remove the terminal setting for the simple examples
below (see more details about runc terminal handling).
Your process field in the config.json should look like this below with "terminal": false and "args": ["sleep", "5"].

text
/* Detailed source-code truncated for AI context efficiency. */

Now we can go through the lifecycle operations in your shell.

bash
# run as root
cd /mycontainer
runc create mycontainerid

# view the container is created and in the "created" state
runc list

# start the process inside the container
runc start mycontainerid

# after 5 seconds view that the container has exited and is now in the stopped state
runc list

# now delete the container
runc delete mycontainerid

This allows higher level systems to augment the containers creation logic with setup of various settings after the container is created and/or before it is deleted. For example, the container's network stack is commonly set up after create but before start.

Rootless containers

runc has the ability to run containers without root privileges. This is called rootless. You need to pass some parameters to runc in order to run rootless containers. See below and compare with the previous version.

Note: In order to use this feature, "User Namespaces" must be compiled and enabled in your kernel. There are various ways to do this depending on your distribution:

  • Confirm CONFIG_USER_NS=y is set in your kernel configuration (normally found in /proc/config.gz)
  • Arch/Debian: echo 1 > /proc/sys/kernel/unprivileged_userns_clone
  • RHEL/CentOS 7: echo 28633 > /proc/sys/user/max_user_namespaces

Run the following commands as an ordinary user:

bash
# Same as the first example
mkdir ~/mycontainer
cd ~/mycontainer
mkdir rootfs
docker export $(docker create busybox) | tar -C rootfs -xvf -

# The --rootless parameter instructs runc spec to generate a configuration for a rootless container, which will allow you to run the container as a non-root user.
runc spec --rootless

# The --root parameter tells runc where to store the container state. It must be writable by the user.
runc --root /tmp/runc run mycontainerid

Supervisors

runc can be used with process supervisors and init systems to ensure that containers are restarted when they exit.
An example systemd unit file looks something like this.

systemd
[Unit]
Description=Start My Container

[Service]
Type=forking
ExecStart=/usr/local/sbin/runc run -d --pid-file /run/mycontainerid.pid mycontainerid
ExecStopPost=/usr/local/sbin/runc delete mycontainerid
WorkingDirectory=/mycontainer
PIDFile=/run/mycontainerid.pid

[Install]
WantedBy=multi-user.target

More documentation

License

The code and docs are released under the Apache 2.0 license.


Overview (`libcontainer/README.md`)

libcontainer

Libcontainer provides a native Go implementation for creating containers
with namespaces, cgroups, capabilities, and filesystem access controls.
It allows you to manage the lifecycle of the container performing additional operations
after the container is created.

Container

A container is a self contained execution environment that shares the kernel of the
host system and which is (optionally) isolated from other containers in the system.

Using libcontainer

For a brief overview of using libcontainer, see example_test.go.

Container init

Because containers are spawned in a two step process you will need a binary that
will be executed as the init process for the container. In libcontainer, we use
the current binary (/proc/self/exe) to be executed as the init process, and use
arg "init", we call the first step process "bootstrap", so you always need a "init"
function as the entry of "bootstrap".

In addition to the go init function the early stage bootstrap is handled by importing
nsenter.

For details on how runc implements such "init", see
../init.go and init_linux.go.

Checkpoint & Restore

libcontainer now integrates CRIU for checkpointing and restoring containers.
This lets you save the state of a process running inside a container to disk, and then restore
that state into a new process, on the same machine or on another machine.

criu version 1.5.2 or higher is required to use checkpoint and restore.
If you don't already have criu installed, you can build it from source, following the
online instructions. criu is also installed in the docker image
generated when building libcontainer with docker.

Code and documentation copyright 2014 Docker, inc.
The code and documentation are released under the Apache 2.0 license.
The documentation is also released under Creative Commons Attribution 4.0 International License.
You may obtain a copy of the license, titled CC-BY-4.0, at http://creativecommons.org/licenses/by/4.0/.


Overview (`man/README.md`)

runc man pages

This directory contains man pages for runc in markdown format.

To generate man pages from it, use this command

text
./md2man-all.sh

You will see man pages generated under the man8 directory.


Overview (`libcontainer/nsenter/README.md`)

nsenter

The nsenter package registers a special init constructor that is called before
the Go runtime has a chance to boot. This provides us the ability to setns on
existing namespaces and avoid the issues that the Go runtime has with multiple
threads. This constructor will be called if this package is registered,
imported, in your go application.

The nsenter package will import "C" and it uses cgo
package. In cgo, if the import of "C" is immediately preceded by a comment, that comment,
called the preamble, is used as a header when compiling the C parts of the package.
So every time we import package nsenter, the C code function nsexec() would be
called. And package nsenter is only imported in init.go, so every time the runc
init command is invoked, that C code is run.

Because nsexec() must be run before the Go runtime in order to use the
Linux kernel namespace, you must import this library into a package if
you plan to use libcontainer directly. Otherwise Go will not execute
the nsexec() constructor, which means that the re-exec will not cause
the namespaces to be joined. You can import it like this:

go
import _ "github.com/opencontainers/runc/libcontainer/nsenter"

nsexec() will first get the file descriptor number for the init pipe
from the environment variable _LIBCONTAINER_INITPIPE (which was opened
by the parent and kept open across the fork-exec of the nsexec() init
process). The init pipe is used to read bootstrap data (namespace paths,
clone flags, uid and gid mappings, and the console path) from the parent
process. nsexec() will then call setns(2) to join the namespaces
provided in the bootstrap data (if available), clone(2) a child process
with the provided clone flags, update the user and group ID mappings, do
some further miscellaneous setup steps, and then send the PID of the
child process to the parent of the nsexec() "caller". Finally,
the parent nsexec() will exit and the child nsexec() process will
return to allow the Go runtime take over.

NOTE: We do both setns(2) and clone(2) even if we don't have any
CLONE_NEW* clone flags because we must fork a new process in order to
enter the PID namespace.


Overview (`tests/cmd/README.md`)

These are helpers used by integration tests.

They are compiled from the top-level Makefile via make test-binaries,
and the resulting binaries can be found in ./_bin.


Overview (`tests/integration/README.md`)

runc Integration Tests

Integration tests provide end-to-end testing of runc.

Note that integration tests do not replace unit tests.

As a rule of thumb, code should be tested thoroughly with unit tests.
Integration tests on the other hand are meant to test a specific feature end
to end.

Integration tests are written in bash using the
bats (Bash Automated Testing System)
framework. Please see
bats documentation
for more details.

Running integration tests

The easiest way to run integration tests is with Docker:

bash
make integration

Alternatively, you can run integration tests directly on your host through make:

bash
sudo make localintegration

Or you can just run them directly using bats

bash
sudo bats tests/integration

To run a single test bucket:

bash
make integration TESTPATH="/checkpoint.bats"

To run them on your host, you need to set up a development environment plus
bats (Bash Automated Testing System).

For example:

bash
cd ~/go/src/github.com
git clone https://github.com/bats-core/bats-core.git
cd bats-core
./install.sh /usr/local

Writing integration tests

Helper functions
are provided in order to facilitate writing tests.

Call runc (and any other command) via bats' run helper, giving the expected
exit code, and check its output with the assertion helpers:

bash
run -0 runc list -q
assert_line --index 0 "test_box1"

run ! runc state test_busybox
assert_output --partial "does not exist"

The -N argument to run makes bats fail the test unless the command exits
with code N, and run ! unless it fails with any non-zero code. Use run -0
for a command that has to succeed; a bare run accepts any exit code, so use
it only where the exit code is genuinely irrelevant.

The available assertions, a subset of the
bats-assert API implemented in
lib/assert.bash,
are:

bash
assert_output [--partial | --regexp] EXPECTED
refute_output [--partial | --regexp] UNEXPECTED
assert_line --index IDX [--partial | --regexp] EXPECTED

Prefer these over [ ... ] or [[ ... ]] comparisons: when an assertion
fails, the test log shows both the expected and the actual value, while a bare
test expression only shows the expression itself.

Please see existing tests for examples.


Overview (`tests/cmd/seccompagent/README.md`)

Seccomp Agent

Warning

Please note this is an example agent, as such it is possible that specially
crafted messages can produce bad behaviour. Please use it as an example only.

Also, this agent is used for integration tests. Be aware that changing the
behaviour can break the integration tests.

Get started

Compile runc and seccompagent:

bash
make runc seccompagent

Run the seccomp agent in the background:

bash
sudo ./tests/cmd/_bin/seccompagent &

Prepare a container:

bash
mkdir container-seccomp-notify
cd container-seccomp-notify
mkdir rootfs
docker export $(docker create busybox) | tar -C rootfs -xvf -

Then, generate a config.json by running the script gen-seccomp-example-cfg.sh
from the directory where this README.md is in the container directory you
prepared earlier (container-seccomp-notify).

Then start the container:

bash
runc run mycontainerid

The container will output something like this:

bash
+ cd /dev/shm
+ mkdir test-dir
+ touch test-file
+ chmod 777 test-file
chmod: changing permissions of 'test-file': No medium found
+ stat /dev/shm/test-dir-foo
  File: /dev/shm/test-dir-foo
  Size: 40        	Blocks: 0          IO Block: 4096   directory
Device: 3eh/62d	Inode: 2           Links: 2
Access: (0755/drwxr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2021-09-09 15:03:13.043716040 +0000
Modify: 2021-09-09 15:03:13.043716040 +0000
Change: 2021-09-09 15:03:13.043716040 +0000
 Birth: -
+ ls -l /dev/shm
total 0
drwxr-xr-x 2 root root 40 Sep  9 15:03 test-dir-foo
-rw-r--r-- 1 root root  0 Sep  9 15:03 test-file
+ echo Note the agent added a suffix for the directory name and chmod fails
Note the agent added a suffix for the directory name and chmod fails

This shows a simple example that runs in /dev/shm just because it is a tmpfs in
the example config.json.

The agent makes all chmod calls fail with ENOMEDIUM, as the example output shows.

For mkdir, the agent adds a "-foo" suffix: the container runs "mkdir test-dir"
but the directory created is "test-dir-foo".


Overview (`vendor/github.com/aperturerobotics/protobuf-go-lite/README.md`)

protobuf-go-lite

GoDoc Widget Go Report Card Widget DeepWiki Widget

protobuf-go-lite is a stripped-down version of the protobuf-go code
generator modified to work without reflection and merged with vtprotobuf to
provide modular features with static code generation for marshal/unmarshal,
size, clone, equal, text, and JSON. JSON support is derived from a fork of
protoc-gen-go-json.

Static code generation without reflection is more efficient at runtime and
results in smaller code binaries. It also provides better support for tinygo
which has limited reflection support.

protobuf-go-lite supports Edition 2024 schemas that resolve to the open Go API
and static, reflect-free generated output. The default features=all path
supports explicit and implicit presence, legacy required fields, packed
encoding, delimited message encoding, oneofs, maps, clone/equal, text,
unmarshal, unsafe unmarshal, and JSON when the resolved JSON format is
ALLOW.

Generated output is static Go code. The default codegen=helper mode emits
message methods that call small concrete helpers from the root
protobuf-go-lite runtime package to keep generated .pb.go files smaller.
The fallback codegen=unrolled mode keeps the older inline method-body shape
for helper-converted method families when callers need to inspect or compare
that output. Neither mode relies on Go reflection, descriptors, struct tags, or
runtime type metadata for generated marshal, unmarshal, size, clone, equal,
text, or JSON behavior.

protobuf-go-lite rejects Edition schemas that require closed enum semantics,
LEGACY_BEST_EFFORT JSON, or explicit hybrid/opaque Go APIs. It does not
support fieldmasks and extensions.

Ecosystem

Lightweight Protobuf 3 RPCs are implemented in StaRPC for Go and TypeScript.

protoc-gen-doc is recommended for generating documentation.

protobuf-es-lite is recommended for lightweight TypeScript protobufs.

Protobuf

protocol buffers are a cross-platform cross-language
message serialization format. Protobuf is a language for specifying the schema
for structured data. This schema is compiled into language specific bindings.
This project provides both a tool to generate Go code for the protocol buffer
language, and also the runtime implementation to handle serialization of
messages in Go.

See the protocol buffer developer guide for
more information about protocol buffers themselves.

Example

See the protobuf-project
template for an example of how to use this package and vtprotobuf together with
protowrap to generate protobufs for your project.

This package is available at github.com/aperturerobotics/protobuf-go-lite.

Package index

Summary of the packages provided by this module:

Usage

  1. Install protoc-gen-go-lite:

    text
    go install github.com/aperturerobotics/protobuf-go-lite/cmd/protoc-gen-go-lite@latest
  2. Update your protoc generator to use the new plug-in.

    text
    for name in $(PROTO_SRC_NAMES); do \
        protoc \
          --plugin protoc-gen-go-lite="${GOBIN}/protoc-gen-go-lite" \
          --go-lite_out=.  \
          --go-lite_opt=features=marshal+unmarshal+size+equal+clone+text \
        proto/$${name}.proto; \
    done

protobuf-go-lite replaces protoc-gen-go and protoc-gen-go-vtprotobuf and should not be used with those generators.

Check out the template for a quick start!

Code generation modes

protoc-gen-go-lite accepts codegen=helper and codegen=unrolled:

  • codegen=helper is the default. It emits static message methods that call
    concrete runtime helpers such as encode, decode, clone/equal, size, and text
    helpers.
  • codegen=unrolled emits the older inline method-body style for
    helper-converted method families. Select it by adding codegen=unrolled to
    --go-lite_opt.

Both modes are selected at generation time and produce normal Go packages for
callers. They do not add a reflection registry, descriptor builder, struct tag
interpreter, or runtime type-metadata dependency to generated fast paths.

Generated output

Generated .pb.go files are checked in for this repository's fixtures and
well-known type packages. After changing the generator or runtime helpers,
regenerate and verify them with the GNU Make targets:

text
gmake gengo
gmake check-gengo

On systems where make is GNU Make, make check-gengo is equivalent. On macOS,
use gmake so the same GNU Make behavior is used locally and in Linux CI.

Available features

The following additional features can be enabled:

  • size: generates a func (p *YourProto) SizeVT() int helper that behaves identically to calling proto.Size(p) on the message, except the size calculation is static generated code and does not use reflection. This helper function can be used directly, and it'll also be used by the marshal codegen to ensure the destination buffer is properly sized before ProtoBuf objects are marshalled to it.

  • equal: generates the following helper methods

    • func (this *YourProto) EqualVT(that *YourProto) bool: this function behaves almost identically to calling proto.Equal(this, that) on messages, except the equality calculation is static generated code and does not use reflection. This helper function can be used directly.

    • func (this *YourProto) EqualMessageVT(thatMsg any) bool: this function behaves like the above this.EqualVT(that), but allows comparing against arbitrary proto messages. If thatMsg is not of type *YourProto, false is returned. The uniform signature provided by this method allows accessing this method via type assertions even if the message type is not known at compile time. This allows implementing a generic func EqualVT(proto.Message, proto.Message) bool without reflection.

  • marshal: generates the following helper methods

    • func (p *YourProto) MarshalVT() ([]byte, error): this function behaves identically to calling proto.Marshal(p), except the actual marshalling is static generated code and does not use reflection or allocate memory. This function simply allocates a properly sized buffer by calling SizeVT on the message and then uses MarshalToSizedBufferVT to marshal to it.

    • func (p *YourProto) MarshalToVT(data []byte) (int, error): this function can be used to marshal a message to an existing buffer. The buffer must be large enough to hold the marshalled message, otherwise this function will panic. It returns the number of bytes marshalled. This function is useful e.g. when using memory pooling to re-use serialization buffers.

    • func (p *YourProto) MarshalToSizedBufferVT(data []byte) (int, error): this function behaves like MarshalTo but expects that the input buffer has the exact size required to hold the message, otherwise it will panic.

  • marshal_strict: generates the following helper methods

    • func (p *YourProto) MarshalVTStrict() ([]byte, error): this function behaves like MarshalVT, except fields are marshalled in a strict order by field's numbers they were declared in .proto file.

    • func (p *YourProto) MarshalToVTStrict(data []byte) (int, error): this function behaves like MarshalToVT, except fields are marshalled in a strict order by field's numbers they were declared in .proto file.

    • func (p *YourProto) MarshalToSizedBufferVTStrict(data []byte) (int, error): this function behaves like MarshalToSizedBufferVT, except fields are marshalled in a strict order by field's numbers they were declared in .proto file.

  • unmarshal: generates a func (p *YourProto) UnmarshalVT(data []byte) that behaves similarly to calling proto.Unmarshal(data, p) on the message, except the unmarshalling is performed by static generated code without using reflection and allocating as little memory as possible. If the receiver p is not fully zeroed-out, the unmarshal call will actually behave like proto.Merge(data, p). This is because the proto.Unmarshal in the ProtoBuf API is implemented by resetting the destination message and then calling proto.Merge on it. To ensure proper Unmarshal semantics, ensure you've called proto.Reset on your message before calling UnmarshalVT, or that your message has been newly allocated.

  • unmarshal_unsafe generates a func (p *YourProto) UnmarshalVTUnsafe(data []byte) that behaves like UnmarshalVT, except it unsafely casts slices of data to bytes and string fields instead of copying them to newly allocated arrays, so that it performs less allocations. Data received from the wire has to be left untouched for the lifetime of the message. Otherwise, the message's bytes and string fields can be corrupted.

  • clone: generates the following helper methods

    • func (p *YourProto) CloneVT() *YourProto: this function behaves similarly to calling proto.Clone(p) on the message, except the cloning is performed by static generated code without using reflection. If the receiver p is nil a typed nil is returned.

    • func (p *YourProto) CloneMessageVT() any: this function behaves like the above p.CloneVT(), but provides a uniform signature in order to be accessible via type assertions even if the type is not known at compile time. This allows implementing a generic func CloneMessageVT() any without reflection. If the receiver p is nil, a typed nil pointer of the message type will be returned inside a any interface.

  • json: generates the following helper methods

    • func (p *YourProto) UnmarshalJSON(data []byte) error behaves similarly to calling protojson.Unmarshal(data, p) on the message, except the unmarshalling is performed by static generated code without using reflection and allocating as little memory as possible. If the receiver p is not fully zeroed-out, the unmarshal call will actually behave like proto.Merge(data, p). To ensure proper Unmarshal semantics, ensure you've called proto.Reset on your message before calling UnmarshalJSON, or that your message has been newly allocated.

    • func (p *YourProto) UnmarshalJSONValue(val *fastjson.Value) error unmarshals a *fastjson.Value.

    • func (p *YourProto) MarshalJSON() ([]byte, error) behaves similarly to calling protojson.Marshal(p) on the message, except the marshalling is performed by static generated code without using reflection and allocating as little memory as possible.

    • Adding a //protobuf-go-lite:disable-json comment before a message or enum will disable the json marshaler / unmarshaler.

  • text: generates MarshalProtoText() string and String() string methods
    that emit protobuf text-format-style output using static generated code
    without reflection. Adding a //protobuf-go-lite:disable-text comment before
    a message disables text generation for that message.

License

BSD-3


Overview (`vendor/github.com/containerd/console/README.md`)

console



Golang package for dealing with consoles. Light on deps and a simple API.

Modifying the current process

go
current := console.Current()
defer current.Reset()

if err := current.SetRaw(); err != nil {
}
ws, err := current.Size()
current.Resize(ws)

Project details

console is a containerd sub-project, licensed under the Apache 2.0 license.
As a containerd sub-project, you will find the:

information in our containerd/project repository.


Overview (`vendor/github.com/cyphar/filepath-securejoin/README.md`)

`filepath-securejoin`


Old API

This library was originally just an implementation of SecureJoin which was
intended to be included in the Go standard library as a safer
filepath.Join that would restrict the path lookup to be inside a root
directory.

The implementation was based on code that existed in several container
runtimes. Unfortunately, this API is fundamentally unsafe against attackers
that can modify path components after SecureJoin returns and before the
caller uses the path, allowing for some fairly trivial TOCTOU attacks.

SecureJoin (and SecureJoinVFS) are still provided by this library to
support legacy users, but new users are strongly suggested to avoid using
SecureJoin and instead use the new api or switch to
libpathrs.

With the above limitations in mind, this library guarantees the following:

  • If no error is set, the resulting string must be a child path of
    root and will not contain any symlink path components (they will all be
    expanded).

  • When expanding symlinks, all symlink path components must be resolved
    relative to the provided root. In particular, this can be considered a
    userspace implementation of how chroot(2) operates on file paths. Note that
    these symlinks will not be expanded lexically (filepath.Clean is not
    called on the input before processing).

  • Non-existent path components are unaffected by SecureJoin (similar to
    filepath.EvalSymlinks's semantics).

  • The returned path will always be filepath.Cleaned and thus not contain any
    .. components.

A (trivial) implementation of this function on GNU/Linux systems could be done
with the following (note that this requires root privileges and is far more
opaque than the implementation in this library, and also requires that
readlink is inside the root path and is trustworthy):

go
package securejoin

import (
	"os/exec"
	"path/filepath"
)

func SecureJoin(root, unsafePath string) (string, error) {
	unsafePath = string(filepath.Separator) + unsafePath
	cmd := exec.Command("chroot", root,
		"readlink", "--canonicalize-missing", "--no-newline", unsafePath)
	output, err := cmd.CombinedOutput()
	if err != nil {
		return "", err
	}
	expanded := string(output)
	return filepath.Join(root, expanded), nil
}

New API

While we recommend users switch to libpathrs as soon as it has a
stable release, some methods implemented by libpathrs have been ported to this
library to ease the transition. These APIs are only supported on Linux.

These APIs are implemented such that filepath-securejoin will
opportunistically use certain newer kernel APIs that make these operations far
more secure. In particular:

  • All of the lookup operations will use openat2 on new enough
    kernels (Linux 5.6 or later) to restrict lookups through magic-links and
    bind-mounts (for certain operations) and to make use of RESOLVE_IN_ROOT to
    efficiently resolve symlinks within a rootfs.

  • The APIs provide hardening against a malicious /proc mount to either detect
    or avoid being tricked by a /proc that is not legitimate. This is done
    using openat2 for all users, and privileged users will also be
    further protected by using fsopen and open_tree
    (Linux 5.2 or later).

`OpenInRoot`

go
func OpenInRoot(root, unsafePath string) (*os.File, error)
func OpenatInRoot(root *os.File, unsafePath string) (*os.File, error)
func Reopen(handle *os.File, flags int) (*os.File, error)

OpenInRoot is a much safer version of

go
path, err := securejoin.SecureJoin(root, unsafePath)
file, err := os.OpenFile(path, unix.O_PATH|unix.O_CLOEXEC)

that protects against various race attacks that could lead to serious security
issues, depending on the application. Note that the returned *os.File is an
O_PATH file descriptor, which is quite restricted. Callers will probably need
to use Reopen to get a more usable handle (this split is done to provide
useful features like PTY spawning and to avoid users accidentally opening bad
inodes that could cause a DoS).

Callers need to be careful in how they use the returned *os.File. Usually it
is only safe to operate on the handle directly, and it is very easy to create a
security issue. libpathrs provides far more helpers to make using
these handles safer -- there is currently no plan to port them to
filepath-securejoin.

OpenatInRoot is like OpenInRoot except that the root is provided using an
*os.File. This allows you to ensure that multiple OpenatInRoot (or
MkdirAllHandle) calls are operating on the same rootfs.

NOTE: Unlike SecureJoin, OpenInRoot will error out as soon as it hits
a dangling symlink or non-existent path. This is in contrast to SecureJoin
which treated non-existent components as though they were real directories,
and would allow for partial resolution of dangling symlinks. These behaviours
are at odds with how Linux treats non-existent paths and dangling symlinks,
and so these are no longer allowed.

`MkdirAll`

go
func MkdirAll(root, unsafePath string, mode int) error
func MkdirAllHandle(root *os.File, unsafePath string, mode int) (*os.File, error)

MkdirAll is a much safer version of

go
path, err := securejoin.SecureJoin(root, unsafePath)
err = os.MkdirAll(path, mode)

that protects against the same kinds of races that OpenInRoot protects
against.

MkdirAllHandle is like MkdirAll except that the root is provided using an
*os.File (the reason for this is the same as with OpenatInRoot) and an
*os.File of the final created directory is returned (this directory is
guaranteed to be effectively identical to the directory created by
MkdirAllHandle, which is not possible to ensure by just using OpenatInRoot
after MkdirAll).

NOTE: Unlike SecureJoin, MkdirAll will error out as soon as it hits
a dangling symlink or non-existent path. This is in contrast to SecureJoin
which treated non-existent components as though they were real directories,
and would allow for partial resolution of dangling symlinks. These behaviours
are at odds with how Linux treats non-existent paths and dangling symlinks,
and so these are no longer allowed. This means that MkdirAll will not
create non-existent directories referenced by a dangling symlink.

License

SPDX-License-Identifier: BSD-3-Clause AND MPL-2.0

Some of the code in this project is derived from Go, and is licensed under a
BSD 3-clause license (available in LICENSE.BSD). Other files (many of which
are derived from libpathrs) are licensed under the Mozilla Public
License version 2.0 (available in LICENSE.MPL-2.0). If you are using the
"New API" described above, you are probably using code from files
released under this license.

Every source file in this project has a copyright header describing its
license. Please check the license headers of each file to see what license
applies to it.

See COPYING.md for some more details.


Overview (`vendor/github.com/docker/go-units/README.md`)

Introduction

go-units is a library to transform human friendly measurements into machine friendly values.

Usage

See the docs in godoc for examples and documentation.

Copyright ยฉ 2015 Docker, Inc.

go-units is licensed under the Apache License, Version 2.0.
See LICENSE for the full text of the license.


Overview (`vendor/github.com/mrunalp/fileutils/README.md`)

fileutils

Collection of utilities for file manipulation in golang

The library is based on docker pkg/archive pkg/idtools but does copies instead of handling archive formats.


Overview (`vendor/github.com/opencontainers/cgroups/README.md`)

OCI Project Template

Useful boilerplate and organizational information for all OCI projects.


Overview (`vendor/github.com/seccomp/libseccomp-golang/README.md`)

===============================================================================
https://github.com/seccomp/libseccomp-golang



The libseccomp library provides an easy to use, platform independent, interface
to the Linux Kernel's syscall filtering mechanism. The libseccomp API is
designed to abstract away the underlying BPF based syscall filter language and
present a more conventional function-call based filtering interface that should
be familiar to, and easily adopted by, application developers.

The libseccomp-golang library provides a Go based interface to the libseccomp
library.

Online Resources

The library source repository currently lives on GitHub at the following URLs:

Documentation for this package is also available at:

Verifying Releases

Starting with libseccomp-golang v0.10.0, the git tag corresponding to each
release should be signed by one of the libseccomp-golang maintainers. It is
recommended that before use you verify the release tags using the following
command:

text
% git tag -v <tag>

At present, only the following keys, specified via the fingerprints below, are
authorized to sign official libseccomp-golang release tags:

text
Paul Moore 
7100 AADF AE6E 6E94 0D2E  0AD6 55E4 5A5A E8CA 7C8A

Tom Hromatka <[email protected]>
47A6 8FCE 37C7 D702 4FD6  5E11 356C E62C 2B52 4099

Kir Kolyshkin <[email protected]>
C242 8CD7 5720 FACD CF76  B6EA 17DE 5ECB 75A1 100E

More information on GnuPG and git tag verification can be found at their
respective websites: https://git-scm.com/docs/git and https://gnupg.org.

Installing the package

text
% go get github.com/seccomp/libseccomp-golang

Contributing

See CONTRIBUTING.md.


Overview (`vendor/github.com/sirupsen/logrus/README.md`)

Logrus [](https://github.com/sirupsen/logrus/actions?query=workflow%3ACI) [](https://pkg.go.dev/github.com/sirupsen/logrus)

Logrus is a structured logger for Go (golang), completely API compatible with
the standard library logger.

Logrus is in maintenance mode. The project focuses on security, bug fixes,
and performance improvements. New features are not planned, aside from changes
required to provide interoperability with other logging ecosystems (e.g., Go's
log/slog).

I believe Logrus' biggest contribution is to have played a part in today's
widespread use of structured logging in Golang. There doesn't seem to be a
reason to do a major, breaking iteration into Logrus V2, since the fantastic Go
community has built those independently. Many fantastic alternatives have sprung
up. Logrus would look like those, had it been re-designed with what we know
about structured logging in Go today. Check out, for example,
Zerolog, Zap, and Apex.

Nicely color-coded in development (when a TTY is attached, otherwise just
plain text):

With logrus.SetFormatter(&logrus.JSONFormatter{}), for easy parsing by logstash
or Splunk:

json lines
{"animal":"walrus","level":"info","msg":"A group of walrus emerges from the ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"}
{"level":"warning","msg":"The group's number increased tremendously!","number":122,"omg":true,"time":"2014-03-10 19:57:38.562471297 -0400 EDT"}
{"animal":"walrus","level":"info","msg":"A giant walrus appears!","size":10,"time":"2014-03-10 19:57:38.562500591 -0400 EDT"}
{"animal":"walrus","level":"info","msg":"Tremendously sized cow enters the ocean.","size":9,"time":"2014-03-10 19:57:38.562527896 -0400 EDT"}
{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true,"time":"2014-03-10 19:57:38.562543128 -0400 EDT"}

With the default logrus.SetFormatter(&logrus.TextFormatter{}) when a TTY is not
attached, the output is compatible with the
logfmt format:

bash
time="2015-03-26T01:27:38-04:00" level=debug msg="Started observing beach" animal=walrus number=8
time="2015-03-26T01:27:38-04:00" level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10
time="2015-03-26T01:27:38-04:00" level=warning msg="The group's number increased tremendously!" number=122 omg=true
time="2015-03-26T01:27:38-04:00" level=debug msg="Temperature changes" temperature=-4
time="2015-03-26T01:27:38-04:00" level=panic msg="It's over 9000!" animal=orca size=9009
time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" animal=orca err="It's over 9000!" number=100 omg=true size=9009

To ensure this behaviour even if a TTY is attached, set your formatter as follows:

go
logrus.SetFormatter(&logrus.TextFormatter{
    DisableColors: true,
    FullTimestamp: true,
})

Logging Method Name

If you wish to add the calling method as a field, instruct the logger via:

go
logrus.SetReportCaller(true)

This adds the caller as 'method' like so:

json
{"animal":"penguin","level":"fatal","method":"github.com/sirupsen/arcticcreatures.migrate","msg":"a penguin swims by","time":"2014-03-10 19:57:38.562543129 -0400 EDT"}
bash
time="2015-03-26T01:27:38-04:00" level=fatal method=github.com/sirupsen/arcticcreatures.migrate msg="a penguin swims by" animal=penguin

Note that this does add measurable overhead - the cost will depend on the version of Go, but is
between 20 and 40% in recent tests with 1.6 and 1.7. You can validate this in your
environment via benchmarks:

bash
go test -bench=ReportCaller

Case-sensitivity

The organization's name was changed to lower-case. If you are getting import
conflicts due to case sensitivity, please use the lower-case import:
github.com/sirupsen/logrus.

Example

The simplest way to use Logrus is simply the package-level exported logger:

go
package main

import "github.com/sirupsen/logrus"

func main() {
  logrus.WithFields(logrus.Fields{
    "animal": "walrus",
  }).Info("A walrus appears")
}

Note that it's completely api-compatible with the stdlib logger, so you can
replace your log imports everywhere with log "github.com/sirupsen/logrus"
and you'll now have the flexibility of Logrus. You can customize it all you
want:

go
package main

import (
  "os"

  log "github.com/sirupsen/logrus"
)

func init() {
  // Log as JSON instead of the default ASCII formatter.
  log.SetFormatter(&log.JSONFormatter{})

  // Output to stdout instead of the default stderr
  // Can be any io.Writer, see below for File example
  log.SetOutput(os.Stdout)

  // Only log the warning severity or above.
  log.SetLevel(log.WarnLevel)
}

func main() {
  log.WithFields(log.Fields{
    "animal": "walrus",
    "size":   10,
  }).Info("A group of walrus emerges from the ocean")

  log.WithFields(log.Fields{
    "omg":    true,
    "number": 122,
  }).Warn("The group's number increased tremendously!")

  log.WithFields(log.Fields{
    "omg":    true,
    "number": 100,
  }).Fatal("The ice breaks!")

  // A common pattern is to re-use fields between logging statements by re-using
  // the logrus.Entry returned from WithFields()
  contextLogger := log.WithFields(log.Fields{
    "common": "this is a common field",
    "other": "I also should be logged always",
  })

  contextLogger.Info("I'll be logged with common and other field")
  contextLogger.Info("Me too")
}

For more advanced usage such as logging to multiple locations from the same
application, you can also create an instance of the logrus Logger:

go
package main

import (
  "os"

  "github.com/sirupsen/logrus"
)

// Create a new instance of the logger. You can have any number of instances.
var logger = logrus.New()

func main() {
  // The API for setting attributes is a little different than the package level
  // exported logger. See Godoc. 
  logger.Out = os.Stdout

  // You could set this to any `io.Writer` such as a file
  // file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
  // if err == nil {
  //  logger.Out = file
  // } else {
  //  logger.Info("Failed to log to file, using default stderr")
  // }

  logger.WithFields(logrus.Fields{
    "animal": "walrus",
    "size":   10,
  }).Info("A group of walrus emerges from the ocean")
}

Fields

Logrus encourages careful, structured logging through logging fields instead of
long, unparseable error messages. For example, instead of: logrus.Fatalf("Failed to send event %s to topic %s with key %d"), you should log the much more
discoverable:

go
logrus.WithFields(logrus.Fields{
  "event": event,
  "topic": topic,
  "key": key,
}).Fatal("Failed to send event")

We've found this API forces you to think about logging in a way that produces
much more useful logging messages. We've been in countless situations where just
a single added field to a log statement that was already there would've saved us
hours. The WithFields call is optional.

In general, with Logrus using any of the printf-family functions should be
seen as a hint you should add a field, however, you can still use the
printf-family functions with Logrus.

Default Fields

Often it's helpful to have fields always attached to log statements in an
application or parts of one. For example, you may want to always log the
request_id and user_ip in the context of a request. Instead of writing
logger.WithFields(logrus.Fields{"request_id": request_id, "user_ip": user_ip}) on
every line, you can create a logrus.Entry to pass around instead:

go
requestLogger := logger.WithFields(logrus.Fields{"request_id": request_id, "user_ip": user_ip})
requestLogger.Info("something happened on that request") // will log request_id and user_ip
requestLogger.Warn("something not great happened")

Hooks

You can add hooks for logging levels. For example to send errors to an exception
tracking service on Error, Fatal and Panic, info to StatsD or log to
multiple places simultaneously, e.g. syslog.

Logrus comes with built-in hooks. Add those, or your custom hook, in
init:

go
package main

import (
  "log/syslog"

  "github.com/sirupsen/logrus"
  airbrake "gopkg.in/gemnasium/logrus-airbrake-hook.v2"
  logrus_syslog "github.com/sirupsen/logrus/hooks/syslog"
)

func init() {

  // Use the Airbrake hook to report errors that have Error severity or above to
  // an exception tracker. You can create custom hooks, see the Hooks section.
  logrus.AddHook(airbrake.NewHook(123, "xyz", "production"))

  hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
  if err != nil {
    logrus.Error("Unable to connect to local syslog daemon")
  } else {
    logrus.AddHook(hook)
  }
}

Note: Syslog hooks also support connecting to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). For the detail, please check the syslog hook README.

A list of currently known service hooks can be found in this wiki page

Level logging

Logrus has seven logging levels: Trace, Debug, Info, Warning, Error, Fatal and Panic.

go
logrus.Trace("Something very low level.")
logrus.Debug("Useful debugging information.")
logrus.Info("Something noteworthy happened!")
logrus.Warn("You should probably take a look at this.")
logrus.Error("Something failed but I'm not quitting.")
// Calls os.Exit(1) after logging
logrus.Fatal("Bye.")
// Calls panic() after logging
logrus.Panic("I'm bailing.")

You can set the logging level on a Logger, then it will only log entries with
that severity or anything above it:

go
// Will log anything that is info or above (warn, error, fatal, panic). Default.
logrus.SetLevel(logrus.InfoLevel)

It may be useful to set logrus.Level = logrus.DebugLevel in a debug or verbose
environment if your application has that.

Note: If you want different log levels for global (logrus.SetLevel(...)) and syslog logging, please check the syslog hook README.

Entries

Besides the fields added with WithField or WithFields some fields are
automatically added to all logging events:

  1. time. The timestamp when the entry was created.
  2. msg. The logging message passed to {Info,Warn,Error,Fatal,Panic} after
    the AddFields call. E.g. Failed to send event.
  3. level. The logging level. E.g. info.

Environments

Logrus has no notion of environment.

If you wish for hooks and formatters to only be used in specific environments,
you should handle that yourself. For example, if your application has a global
variable Environment, which is a string representation of the environment you
could do:

go
import (
  "github.com/sirupsen/logrus"
)

func init() {
  // do something here to set environment depending on an environment variable
  // or command-line flag
  if Environment == "production" {
    logrus.SetFormatter(&logrus.JSONFormatter{})
  } else {
    // The TextFormatter is default, you don't actually have to do this.
    logrus.SetFormatter(&logrus.TextFormatter{})
  }
}

This configuration is how logrus was intended to be used, but JSON in
production is mostly only useful if you do log aggregation with tools like
Splunk or Logstash.

Formatters

The built-in logging formatters are:

  • logrus.TextFormatter
    logs the event in colors if the logger output is a TTY, otherwise without colors.
    • To force colored output when there is no TTY, set the ForceColors
      field to true. To force no colored output even if there is a TTY set the
      DisableColors field to true.
    • On modern Windows terminals with ANSI (Virtual Terminal) support, TextFormatter
      automatically enables colored output.
    • If your environment does not support ANSI escape sequences, wrap the logger output
      using github.com/mattn/go-colorable
      and set ForceColors (or CLICOLOR_FORCE=1) to enable colors through the wrapper.
    • When colors are enabled, levels are truncated to 4 characters by default. To disable
      truncation set the DisableLevelTruncation field to true.
    • When outputting to a TTY, it's often helpful to visually scan down a column where all the levels are the same width. Setting the PadLevelText field to true enables this behavior, by adding padding to the level text.
  • logrus.JSONFormatter
    logs fields as JSON.

Third-party logging formatters:

You can define your formatter by implementing the Formatter interface,
requiring a Format method. Format takes an *Entry. entry.Data is a
Fields type (map[string]any) with all your fields as well as the
default ones (see Entries section above):

go
type MyJSONFormatter struct{}

logrus.SetFormatter(new(MyJSONFormatter))

func (f *MyJSONFormatter) Format(entry *Entry) ([]byte, error) {
  // Note this doesn't include Time, Level and Message which are available on
  // the Entry. Consult `godoc` on information about those fields or read the
  // source of the official loggers.
  serialized, err := json.Marshal(entry.Data)
    if err != nil {
      return nil, fmt.Errorf("Failed to marshal fields to JSON, %w", err)
    }
  return append(serialized, '\n'), nil
}

Logger as an `io.Writer`

Logrus can be transformed into an io.Writer. That writer is the end of an io.Pipe and it is your responsibility to close it.

go
w := logger.Writer()
defer w.Close()

srv := http.Server{
    // create a stdlib log.Logger that writes to
    // logrus.Logger.
    ErrorLog: log.New(w, "", 0),
}

Each line written to that writer will be printed the usual way, using formatters
and hooks. The level for those entries is info.

This means that we can override the standard library logger easily:

go
logger := logrus.New()
logger.Formatter = &logrus.JSONFormatter{}

// Use logrus for standard log output
// Note that `log` here references stdlib's log
// Not logrus imported under the name `log`.
log.SetOutput(logger.Writer())

Rotation

Log rotation is not provided with Logrus. Log rotation should be done by an
external program (like logrotate(8)) that can compress and delete old log
entries. It should not be a feature of the application-level logger.

Tools

Tool Description
Logrus Mate Logrus mate is a tool for Logrus to manage loggers, you can initial logger's level, hook and formatter by config file, the logger will be generated with different configs in different environments.
Logrus Viper Helper An Helper around Logrus to wrap with spf13/Viper to load configuration with fangs! And to simplify Logrus configuration use some behavior of Logrus Mate. sample

Testing

Logrus has a built-in facility for asserting the presence of log messages. This is implemented through the test hook and provides:

  • decorators for existing logger (test.NewLocal and test.NewGlobal) which basically just adds the test hook
  • a test logger (test.NewNullLogger) that just records log messages (and does not output any):
go
import(
  "testing"

  "github.com/sirupsen/logrus"
  "github.com/sirupsen/logrus/hooks/test"
  "github.com/stretchr/testify/assert"
)

func TestSomething(t*testing.T){
  logger, hook := test.NewNullLogger()
  logger.Error("Helloerror")

  assert.Equal(t, 1, len(hook.Entries))
  assert.Equal(t, logrus.ErrorLevel, hook.LastEntry().Level)
  assert.Equal(t, "Helloerror", hook.LastEntry().Message)

  hook.Reset()
  assert.Nil(t, hook.LastEntry())
}

Fatal handlers

Logrus can register one or more functions that will be called when any fatal
level message is logged. The registered handlers will be executed before
logrus performs an os.Exit(1). This behavior may be helpful if callers need
to gracefully shut down. Unlike a panic("Something went wrong...") call which can be intercepted with a deferred recover a call to os.Exit(1) can not be intercepted.

go
// ...
handler := func() {
  // gracefully shut down something...
}
logrus.RegisterExitHandler(handler)
// ...

Thread safety

By default, Logger is protected by a mutex for concurrent writes. The mutex is held when calling hooks and writing logs.
If you are sure such locking is not needed, you can call logger.SetNoLock() to disable the locking.

Situations when locking is not needed include:

  • You have no hooks registered, or hooks calling is already thread-safe.

  • Writing to logger.Out is already thread-safe, for example:

    1. logger.Out is protected by locks.

    2. logger.Out is an os.File handler opened with O_APPEND flag, and every write is smaller than 4k. (This allows multi-thread/multi-process writing)

      (Refer to http://www.notthewizard.com/2014/06/17/are-files-appends-really-atomic/)


The netlink package provides a simple netlink library for go. Netlink
is the interface a user-space program in linux uses to communicate with
the kernel. It can be used to add and remove interfaces, set ip addresses
and routes, and configure ipsec. Netlink communication requires elevated
privileges, so in most cases this code needs to be run as root. Since
low-level netlink messages are inscrutable at best, the library attempts
to provide an api that is loosely modeled on the CLI provided by iproute2.
Actions like ip link add will be accomplished via a similarly named
function like AddLink(). This library began its life as a fork of the
netlink functionality in
docker/libcontainer but was
heavily rewritten to improve testability, performance, and to add new
functionality like ipsec xfrm handling.

Local Build and Test

You can use go get command:

text
go get github.com/vishvananda/netlink

Testing dependencies:

text
go get github.com/vishvananda/netns

Testing (requires root):

text
sudo -E go test github.com/vishvananda/netlink

Examples

Add a new bridge and add eth1 into it:

go
package main

import (
    "fmt"
    "github.com/vishvananda/netlink"
)

func main() {
    la := netlink.NewLinkAttrs()
    la.Name = "foo"
    mybridge := &netlink.Bridge{LinkAttrs: la}
    err := netlink.LinkAdd(mybridge)
    if err != nil  {
        fmt.Printf("could not add %s: %v\n", la.Name, err)
    }
    eth1, _ := netlink.LinkByName("eth1")
    netlink.LinkSetMaster(eth1, mybridge)
}

Note NewLinkAttrs constructor, it sets default values in structure. For now
it sets only TxQLen to -1, so kernel will set default by itself. If you're
using simple initialization(LinkAttrs{Name: "foo"}) TxQLen will be set to
0 unless you specify it like LinkAttrs{Name: "foo", TxQLen: 1000}.

Add a new ip address to loopback:

go
package main

import (
    "github.com/vishvananda/netlink"
)

func main() {
    lo, _ := netlink.LinkByName("lo")
    addr, _ := netlink.ParseAddr("169.254.169.254/32")
    netlink.AddrAdd(lo, addr)
}

Future Work

Many pieces of netlink are not yet fully supported in the high-level
interface. Aspects of virtually all of the high-level objects don't exist.
Many of the underlying primitives are there, so its a matter of putting
the right fields into the high-level objects and making sure that they
are serialized and deserialized correctly in the Add and List methods.

There are also a few pieces of low level netlink functionality that still
need to be implemented. Routing rules are not in place and some of the
more advanced link types. Hopefully there is decent structure and testing
in place to make these fairly straightforward to add.


Overview (`vendor/github.com/vishvananda/netns/README.md`)

netns - network namespaces in go

The netns package provides an ultra-simple interface for handling
network namespaces in go. Changing namespaces requires elevated
privileges, so in most cases this code needs to be run as root.

Local Build and Test

You can use go get command:

text
go get github.com/vishvananda/netns

Testing (requires root):

text
sudo -E go test github.com/vishvananda/netns

Example

go
package main

import (
    "fmt"
    "net"
    "runtime"

    "github.com/vishvananda/netns"
)

func main() {
    // Lock the OS Thread so we don't accidentally switch namespaces
    runtime.LockOSThread()
    defer runtime.UnlockOSThread()

    // Save the current network namespace
    origns, _ := netns.Get()
    defer origns.Close()

    // Create a new network namespace
    newns, _ := netns.New()
    defer newns.Close()

    // Do something with the network namespace
    ifaces, _ := net.Interfaces()
    fmt.Printf("Interfaces: %v\n", ifaces)

    // Switch back to the original namespace
    netns.Set(origns)
}

Overview (`vendor/github.com/checkpoint-restore/go-criu/v8/README.md`)

go-criu -- Go bindings for CRIU



This repository provides Go bindings for CRIU.
The code is based on the Go-based PHaul implementation from the CRIU repository.
For easier inclusion into other Go projects, the CRIU Go bindings have been
moved to this repository.

CRIU

The Go bindings provide an easy way to use the CRIU RPC calls from Go without
the need to set up all the infrastructure to make the actual RPC connection to CRIU.

The following example would print the version of CRIU:

go
import (
	"log"

	"github.com/checkpoint-restore/go-criu/v8"
)

func main() {
	c := criu.MakeCriu()
	version, err := c.GetCriuVersion()
	if err != nil {
		log.Fatalln(err)
	}
	log.Println(version)
}

or to just check if at least a certain CRIU version is installed:

go
c := criu.MakeCriu()
	result, err := c.IsCriuAtLeast(31100)

CRIT

The crit package provides bindings to decode, encode, and manipulate
CRIU image files natively within Go. It also provides a CLI tool similar
to the original CRIT Python tool. To get started with this, see the docs
at CRIT (Go library).

Releases

The first go-criu release was 3.11 based on CRIU 3.11. The initial plan
was to follow CRIU so that go-criu would carry the same version number as
CRIU.

As go-criu is imported in other projects and as Go modules are expected
to follow Semantic Versioning go-criu will also follow Semantic Versioning
starting with the 4.0.0 release.

The following table shows the relation between go-criu and criu versions:

Major version Latest release CRIU version
v8 8.4.0 4.2
v7 7.2.0 3.19
v7 7.0.0 3.18
v6 6.3.0 3.17
v5 5.3.0 3.16
v5 5.0.0 3.15
v4 4.1.0 3.14

How to contribute

See CONTRIBUTING.md for details on how to contribute to this
project.

Unless mentioned otherwise in a specific file's header, all code in
this project is released under the Apache 2.0 license.

The author of a change remains the copyright holder of their code
(no copyright assignment). The list of authors and contributors can be
retrieved from the git commit history and in some cases, the file headers.


Overview (`vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/README.md`)

`pathrs-lite`

github.com/cyphar/filepath-securejoin/pathrs-lite provides a minimal pure
Go
implementation of the core bits of libpathrs. This is not intended to
be a complete replacement for libpathrs, instead it is mainly intended to be
useful as a transition tool for existing Go projects.

pathrs-lite also provides a very easy way to switch to libpathrs (even for
downstreams where pathrs-lite is being used in a third-party package and is
not interested in using CGo). At build time, if you use the libpathrs build
tag then pathrs-lite will use libpathrs directly instead of the pure Go
implementation. The two backends are functionally equivalent (and we have
integration tests to verify this), so this migration should be very easy with
no user-visible impact.

License

Most of this subpackage is licensed under the Mozilla Public License (version
2.0). For more information, see the top-level COPYING.md and
LICENSE.MPL-2.0 files, as well as the individual license headers for each
file.

text
Copyright (C) 2024-2025 Aleksa Sarai <[email protected]>
Copyright (C) 2024-2025 SUSE LLC

This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at https://mozilla.org/MPL/2.0/.

Overview (`vendor/github.com/godbus/dbus/v5/README.md`)

dbus

dbus is a simple library that implements native Go client bindings for the
D-Bus message bus system.

Features

  • Complete native implementation of the D-Bus message protocol
  • Go-like API (channels for signals / asynchronous method calls, Goroutine-safe connections)
  • Subpackages that help with the introspection / property interfaces

Usage

The complete package documentation and some simple examples are available at
pkg.go.dev. Also, the
_examples directory
gives a short overview over the basic usage.

Projects using godbus

  • bluetooth: cross-platform Bluetooth API for Go and TinyGo.
  • fyne: a cross platform GUI in Go inspired by Material Design.
  • fynedesk: a full desktop environment for Linux/Unix using Fyne.
  • go-systemd: Go bindings to systemd.
  • notify provides desktop notifications over dbus into a library.

Please note that the API is considered unstable for now and may change without
further notice.

License

The library is available under the Simplified BSD License; see LICENSE for the full
text.

Nearly all of the credit for this library goes to https://github.com/guelfey.


Overview (`vendor/github.com/moby/sys/capability/README.md`)

This is a fork of (apparently no longer maintained)
https://github.com/syndtr/gocapability package. It provides basic primitives to
work with Linux capabilities.

For changes, see CHANGELOG.md.

Alternatives


Overview (`vendor/github.com/urfave/cli/v3/README.md`)

Welcome to urfave/cli

Go Reference
Go Report Card
codecov
Tests status

urfave/cli is a declarative, simple, fast, and fun package for building
command line tools in Go featuring:

  • commands and subcommands with alias and prefix match support
  • flexible and permissive help system
  • dynamic shell completion for bash, zsh, fish, and powershell
  • no dependencies except Go standard library
  • input flags for simple types, slices of simple types, time, duration, and
    others
  • compound short flag support (-a -b -c can be shortened to -abc)
  • documentation generation in man and Markdown (supported via the
    urfave/cli-docs module)
  • input lookup from:
    • environment variables
    • plain text files
    • structured file formats (supported via the
      urfave/cli-altsrc module)

Documentation

See the hosted documentation website at https://cli.urfave.org. Contents of
this website are built from the ./docs directory.

Build tags

You can use the following build tags:

`urfave_cli_no_template`

When set, the default help and fish completion are rendered without using
text/template, which makes the Go linker disable dead code elimination (see
Binary Size).
The output is the same, but help templates can not be customized:

  • RootCommandHelpTemplate, CommandHelpTemplate, SubcommandHelpTemplate
    and FishCompletionTemplate are constants;
  • if the default help printer is used with a custom template (such as
    CustomRootCommandHelpTemplate or CustomHelpTemplate), it panics;
  • custom template functions other than wrap and wrapAt are ignored.

Replacing cli.HelpPrinter works as usual, except it receives "root",
"command" or "subcommand" as templ instead of the default template text
(a custom template is passed as is).

Support

Check the Q&A discussions. If you don't find answer to your question, create
a new discussion
.

If you found a bug or have a feature request, create a new issue.

Please keep in mind that this project is run by unpaid volunteers.

License

See LICENSE.


Overview (`vendor/golang.org/x/sys/unix/README.md`)

Building `sys/unix`

The sys/unix package provides access to the raw system call interface of the
underlying operating system. See: https://godoc.org/golang.org/x/sys/unix

Porting Go to a new architecture/OS combination or adding syscalls, types, or
constants to an existing architecture/OS pair requires some manual effort;
however, there are tools that automate much of the process.

Build Systems

There are currently two ways we generate the necessary files. We are currently
migrating the build system to use containers so the builds are reproducible.
This is being done on an OS-by-OS basis. Please update this documentation as
components of the build system change.

Old Build System (currently for `GOOS != "linux"`)

The old build system generates the Go files based on the C header files
present on your system. This means that files
for a given GOOS/GOARCH pair must be generated on a system with that OS and
architecture. This also means that the generated code can differ from system
to system, based on differences in the header files.

To avoid this, if you are using the old build system, only generate the Go
files on an installation with unmodified header files. It is also important to
keep track of which version of the OS the files were generated from (ex.
Darwin 14 vs Darwin 15). This makes it easier to track the progress of changes
and have each OS upgrade correspond to a single change.

To build the files for your current OS and architecture, make sure GOOS and
GOARCH are set correctly and run mkall.sh. This will generate the files for
your specific system. Running mkall.sh -n shows the commands that will be run.

Requirements: bash, go

New Build System (currently for `GOOS == "linux"`)

The new build system uses a Docker container to generate the go files directly
from source checkouts of the kernel and various system libraries. This means
that on any platform that supports Docker, all the files using the new build
system can be generated at once, and generated files will not change based on
what the person running the scripts has installed on their computer.

The OS specific files for the new build system are located in the ${GOOS}
directory, and the build is coordinated by the ${GOOS}/mkall.go program. When
the kernel or system library updates, modify the Dockerfile at
${GOOS}/Dockerfile to checkout the new release of the source.

To build all the files under the new build system, you must be on an amd64/Linux
system and have your GOOS and GOARCH set accordingly. Running mkall.sh will
then generate all of the files for all of the GOOS/GOARCH pairs in the new build
system. Running mkall.sh -n shows the commands that will be run.

Requirements: bash, go, docker

Component files

This section describes the various files used in the code generation process.
It also contains instructions on how to modify these files to add a new
architecture/OS or to add additional syscalls, types, or constants. Note that
if you are using the new build system, the scripts/programs cannot be called normally.
They must be called from within the docker container.

asm files

The hand-written assembly file at asm_${GOOS}_${GOARCH}.s implements system
call dispatch. There are three entry points:

text
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)
  func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
  func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)

The first and second are the standard ones; they differ only in how many
arguments can be passed to the kernel. The third is for low-level use by the
ForkExec wrapper. Unlike the first two, it does not call into the scheduler to
let it know that a system call is running.

When porting Go to a new architecture/OS, this file must be implemented for
each GOOS/GOARCH pair.

mksysnum

Mksysnum is a Go program located at ${GOOS}/mksysnum.go (or mksysnum_${GOOS}.go
for the old system). This program takes in a list of header files containing the
syscall number declarations and parses them to produce the corresponding list of
Go numeric constants. See zsysnum_${GOOS}_${GOARCH}.go for the generated
constants.

Adding new syscall numbers is mostly done by running the build on a sufficiently
new installation of the target OS (or updating the source checkouts for the
new build system). However, depending on the OS, you may need to update the
parsing in mksysnum.

mksyscall.go

The syscall.go, syscall_${GOOS}.go, syscall_${GOOS}_${GOARCH}.go are
hand-written Go files which implement system calls (for unix, the specific OS,
or the specific OS/Architecture pair respectively) that need special handling
and list //sys comments giving prototypes for ones that can be generated.

The mksyscall.go program takes the //sys and //sysnb comments and converts
them into syscalls. This requires the name of the prototype in the comment to
match a syscall number in the zsysnum_${GOOS}_${GOARCH}.go file. The function
prototype can be exported (capitalized) or not.

Adding a new syscall often just requires adding a new //sys function prototype
with the desired arguments and a capitalized name so it is exported. However, if
you want the interface to the syscall to be different, often one will make an
unexported //sys prototype, and then write a custom wrapper in
syscall_${GOOS}.go.

types files

For each OS, there is a hand-written Go file at ${GOOS}/types.go (or
types_${GOOS}.go on the old system). This file includes standard C headers and
creates Go type aliases to the corresponding C types. The file is then fed
through godef to get the Go compatible definitions. Finally, the generated code
is fed though mkpost.go to format the code correctly and remove any hidden or
private identifiers. This cleaned-up code is written to
ztypes_${GOOS}_${GOARCH}.go.

The hardest part about preparing this file is figuring out which headers to
include and which symbols need to be #defined to get the actual data
structures that pass through to the kernel system calls. Some C libraries
preset alternate versions for binary compatibility and translate them on the
way in and out of system calls, but there is almost always a #define that can
get the real ones.
See types_darwin.go and linux/types.go for examples.

To add a new type, add in the necessary include statement at the top of the
file (if it is not already there) and add in a type alias line. Note that if
your type is significantly different on different architectures, you may need
some #if/#elif macros in your include statements.

mkerrors.sh

This script is used to generate the system's various constants. This doesn't
just include the error numbers and error strings, but also the signal numbers
and a wide variety of miscellaneous constants. The constants come from the list
of include files in the includes_${uname} variable. A regex then picks out
the desired #define statements, and generates the corresponding Go constants.
The error numbers and strings are generated from #include <errno.h>, and the
signal numbers and strings are generated from #include <signal.h>. All of
these constants are written to zerrors_${GOOS}_${GOARCH}.go via a C program,
_errors.c, which prints out all the constants.

To add a constant, add the header that includes it to the appropriate variable.
Then, edit the regex (if necessary) to match the desired constant. Avoid making
the regex too broad to avoid matching unintended constants.

internal/mkmerge

This program is used to extract duplicate const, func, and type declarations
from the generated architecture-specific files listed below, and merge these
into a common file for each OS.

The merge is performed in the following steps:

  1. Construct the set of common code that is identical in all architecture-specific files.
  2. Write this common code to the merged file.
  3. Remove the common code from all architecture-specific files.

Generated files

`zerrors_${GOOS}_${GOARCH}.go`

A file containing all of the system's generated error numbers, error strings,
signal numbers, and constants. Generated by mkerrors.sh (see above).

`zsyscall_${GOOS}_${GOARCH}.go`

A file containing all the generated syscalls for a specific GOOS and GOARCH.
Generated by mksyscall.go (see above).

`zsysnum_${GOOS}_${GOARCH}.go`

A list of numeric constants for all the syscall number of the specific GOOS
and GOARCH. Generated by mksysnum (see above).

`ztypes_${GOOS}_${GOARCH}.go`

A file containing Go types for passing into (or returning from) syscalls.
Generated by godefs and the types file (see above).


Overview (`vendor/github.com/opencontainers/selinux/pkg/pwalkdir/README.md`)

pwalkdir: parallel implementation of filepath.WalkDir

This is a wrapper for filepath.WalkDir
which may speed it up by calling multiple callback functions (WalkDirFunc)
in parallel, utilizing goroutines.

By default, it utilizes 2*runtime.NumCPU() goroutines for callbacks.
This can be changed by using WalkN function which has the additional
parameter, specifying the number of goroutines (concurrency).

pwalk vs pwalkdir

This package is very similar to
pwalk,
but utilizes filepath.WalkDir (added to Go 1.16), which does not call stat(2)
on every entry and is therefore faster (up to 3x, depending on usage scenario).

Users who are OK with requiring Go 1.16+ should switch to this
implementation.

Caveats

Please note the following limitations of this code:

  • Unlike filepath.WalkDir, the order of calls is non-deterministic;

  • Only primitive error handling is supported:

    • fs.SkipDir is not supported;

    • ErrNotExist errors from filepath.WalkDir are silently ignored for any path
      except the top directory (WalkDir argument); any other error is returned to
      the caller of WalkDir;

    • once any error is returned from any walkDirFunc instance, no more calls
      to WalkDirFunc are made, and the error is returned to the caller of WalkDir;

    • if more than one WalkDirFunc instance will return an error, only one
      of such errors will be propagated to and returned by WalkDir, others
      will be silently discarded.

Documentation

For the official documentation, see
https://pkg.go.dev/github.com/opencontainers/selinux/pkg/pwalkdir

Benchmarks

For a WalkDirFunc that consists solely of the return statement, this
implementation is about 15% slower than the standard library's
filepath.WalkDir.

Otherwise (if a WalkDirFunc is actually doing something) this is usually
faster, except when the WalkDirN(..., 1) is used. Run go test -bench .
to see how different operations can benefit from it, as well as how the
level of parallelism affects the speed.


Overview (`vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat/README.md`)

gocompat

This directory contains backports of stdlib functions from later Go versions so
the filepath-securejoin can continue to be used by projects that are stuck with
Go 1.18 support. Note that often filepath-securejoin is added in security
patches for old releases, so avoiding the need to bump Go compiler requirements
is a huge plus to downstreams.

The source code is licensed under the same license as the Go stdlib. See the
source files for the precise license information.


Cgroup V2 (`docs/cgroup-v2.md`)

cgroup v2

runc fully supports cgroup v2 (unified mode) since v1.0.0-rc93.

To use cgroup v2, you might need to change the configuration of the host init system.
The following distributions are known to use cgroup v2 by default:

  • Fedora (since 31)
  • Arch Linux (since April 2021)
  • openSUSE Tumbleweed (since c. 2021)
  • Debian GNU/Linux (since 11)
  • Ubuntu (since 21.10)
  • RHEL and RHEL-like distributions (since 9)

On other systemd-based distros, cgroup v2 can be enabled by adding systemd.unified_cgroup_hierarchy=1 to the kernel cmdline.

Am I using cgroup v2?

Yes if /sys/fs/cgroup/cgroup.controllers is present.

Host Requirements

Kernel

  • Recommended version: 5.2 or later
  • Minimum version: 4.15

Kernel older than 5.2 is not recommended due to lack of freezer.

Notably, kernel older than 4.15 MUST NOT be used (unless you are running containers with user namespaces), as it lacks support for controlling permissions of devices.

Systemd

On cgroup v2 hosts, it is highly recommended to run runc with the systemd cgroup driver (runc --systemd-cgroup), though not mandatory.

The recommended systemd version is 244 or later. Older systemd does not support delegation of cpuset controller.

Make sure you also have the dbus-user-session (Debian/Ubuntu) or dbus-daemon (CentOS/Fedora) package installed, and that dbus is running. On Debian-flavored distros, this can be accomplished like so:

bash
sudo apt install -y dbus-user-session
systemctl --user start dbus

Rootless

On cgroup v2 hosts, rootless runc can talk to systemd to get cgroup permissions to be delegated.

bash
runc spec --rootless
jq '.linux.cgroupsPath="user.slice:runc:foo"' config.json | sponge config.json
runc --systemd-cgroup run foo

The container processes are executed in a cgroup like /user.slice/user-$(id -u).slice/user@$(id -u).service/user.slice/runc-foo.scope.

Configuring delegation

Typically, only memory and pids controllers are delegated to non-root users by default.

console
$ cat /sys/fs/cgroup/user.slice/user-$(id -u).slice/user@$(id -u).service/cgroup.controllers
memory pids

To allow delegation of other controllers, you need to change the systemd configuration as follows:

bash
sudo mkdir -p /etc/systemd/system/[email protected]
cat <<EOF | sudo tee /etc/systemd/system/[email protected]/delegate.conf
[Service]
Delegate=cpu cpuset io memory pids
EOF
sudo systemctl daemon-reload

Checkpoint Restore (`docs/checkpoint-restore.md`)

Checkpoint and Restore

For a basic description about checkpointing and restoring containers with
runc please see runc-checkpoint(8) and
runc-restore(8).

Checkpoint/Restore Annotations

In addition to specifying options on the command-line like it is described
in the man-pages (see above), it is also possible to influence CRIU's
behaviour using CRIU configuration files. For details about CRIU's
configuration file support please see CRIU's wiki.

In addition to CRIU's default configuration files runc tells CRIU to
also evaluate the file /etc/criu/runc.conf. Using the annotation
org.criu.config it is, however, possible to change this additional
CRIU configuration file.

If the annotation org.criu.config is set to an empty string runc
will not pass any additional configuration file to CRIU. With an empty
string it is therefore possible to disable the additional CRIU configuration
file. This can be used to make sure that no additional configuration file
changes CRIU's behaviour accidentally.

If the annotation org.criu.config is set to a non-empty string runc will
pass that string to CRIU to be evaluated as an additional configuration file.
If CRIU cannot open this additional configuration file, it will ignore this
file and continue.

Annotation Example to disable additional CRIU configuration file

text
{
	"ociVersion": "1.0.0",
	"annotations": {
		"org.criu.config": ""
	},
	"process": {

Annotation Example to set a specific CRIU configuration file

text
{
	"ociVersion": "1.0.0",
	"annotations": {
		"org.criu.config": "/etc/special-runc-criu-options"
	},
	"process": {

Deprecated (`docs/deprecated.md`)

Deprecated features

The following features are deprecated:

Feature Deprecation release Removal release
cgroup v1 v1.4.0 (May 2029)
  • The latest release in May 2029 may not necessarily support cgroup v1, but there will be at least one maintained branch with the support for cgroup v1.

Experimental (`docs/experimental.md`)

Experimental features

The following features were experimental in the past:

Feature Experimental release Graduation release
cgroup v2 v1.0.0-rc91 v1.0.0-rc93
The runc features command v1.1.0 v1.2.0
runc-dmz v1.2.0-rc1 Dropped in v1.2.1

Spec Conformance (`docs/spec-conformance.md`)

Spec conformance

This branch of runc implements the OCI Runtime Spec v1.3.0
for the linux platform.

Deviations

runc deviates from the spec in the following ways:

  • ociVersion
    is not validated: runc does not generate an error if it is missing, empty, not
    in SemVer format, or specifies a version not supported by runc (e.g. with a
    different major version).
  • If umask
    is not specified, the umask of the container init process is set to 0022,
    rather than left unchanged from the calling process' umask. For runc exec,
    the umask is left unchanged.

Architectures

The following architectures are supported:

runc binary seccomp
amd64 SCMP_ARCH_X86, SCMP_ARCH_X86_64, SCMP_ARCH_X32
arm64 SCMP_ARCH_ARM, SCMP_ARCH_AARCH64
armel SCMP_ARCH_ARM
armhf SCMP_ARCH_ARM
ppc64le SCMP_ARCH_PPC64LE
riscv64 SCMP_ARCH_RISCV64
s390x SCMP_ARCH_S390, SCMP_ARCH_S390X
loong64 SCMP_ARCH_LOONGARCH64

The runc binary might be compilable for i386, big-endian PPC64,
and several MIPS variants too, but these architectures are not officially supported.


Systemd (`docs/systemd.md`)

systemd cgroup driver

By default, runc creates cgroups and sets cgroup limits on its own (this mode
is known as fs cgroup driver). When --systemd-cgroup global option is given
(as in e.g. runc --systemd-cgroup run ...), runc switches to systemd cgroup
driver. This document describes its features and peculiarities.

systemd unit name and placement

When creating a container, runc requests systemd (over dbus) to create
a transient unit for the container, and place it into a specified slice.

The name of the unit and the containing slice is derived from the container
runtime spec in the following way:

  1. If Linux.CgroupsPath is set, it is expected to be in the form
    [slice]:[prefix]:[name].

    Here slice is a systemd slice under which the container is placed.
    If empty, it defaults to system.slice, except when cgroup v2 is
    used and rootless container is created, in which case it defaults
    to user.slice.

    Note that slice can contain dashes to denote a sub-slice
    (e.g. user-1000.slice is a correct notation, meaning a subslice
    of user.slice), but it must not contain slashes (e.g.
    user.slice/user-1000.slice is invalid).

    A slice of - represents a root slice.

    Next, prefix and name are used to compose the unit name, which
    is -<name>.scope, unless name has .slice suffix, in
    which case prefix is ignored and the name is used as is.
    The default value for both prefix and name is empty string.

  2. If Linux.CgroupsPath is not set or empty, it works the same way as if it
    would be set to :runc:<container-id>. See the description above to see
    what it transforms to.

As described above, a unit being created can either be a scope or a slice.
For a scope, runc specifies its parent slice via a Slice= systemd property,
and also sets Delegate=true. For a slice, runc specifies a weak dependency on
the parent slice via a Wants= property.

Resource limits

runc always enables accounting for all controllers, regardless of any limits
being set. This means it unconditionally sets the following properties for the
systemd unit being created:

  • CPUAccounting=true
  • IOAccounting=true (BlockIOAccounting for cgroup v1)
  • MemoryAccounting=true
  • TasksAccounting=true

The resource limits of the systemd unit are set by runc by translating the
runtime spec resources to systemd unit properties.

Such translation is by no means complete, as there are some cgroup properties
that can not be set via systemd. Therefore, runc systemd cgroup driver is
backed by fs driver (in other words, cgroup limits are first set via systemd
unit properties, and when by writing to cgroupfs files).

The set of runtime spec resources which is translated by runc to systemd unit
properties depends on kernel cgroup version being used (v1 or v2), and on the
systemd version being run. If an older systemd version (which does not support
some resources) is used, runc do not set those resources.

The following tables summarize which properties are translated.

cgroup v1

runtime spec resource systemd property name min systemd version
memory.limit MemoryLimit
cpu.shares CPUShares
blockIO.weight BlockIOWeight
pids.limit TasksMax
cpu.cpus AllowedCPUs v244
cpu.mems AllowedMemoryNodes v244

cgroup v2

runtime spec resource systemd property name min systemd version
memory.limit MemoryMax
memory.reservation MemoryLow
memory.swap MemorySwapMax
cpu.shares CPUWeight
pids.limit TasksMax
cpu.cpus AllowedCPUs v244
cpu.mems AllowedMemoryNodes v244
unified.cpu.max CPUQuota, CPUQuotaPeriodSec v242
unified.cpu.weight CPUWeight
unified.cpu.idle CPUWeight v252
unified.cpuset.cpus AllowedCPUs v244
unified.cpuset.mems AllowedMemoryNodes v244
unified.memory.high MemoryHigh
unified.memory.low MemoryLow
unified.memory.min MemoryMin
unified.memory.max MemoryMax
unified.memory.swap.max MemorySwapMax
unified.pids.max TasksMax

For documentation on systemd unit resource properties, see
systemd.resource-control(5) man page.

Auxiliary properties

Auxiliary properties of a systemd unit (as shown by systemctl show after the container is created) can be set (or overwritten) by
adding annotations to the container runtime spec (config.json).

For example:

json
"annotations": {
                "org.systemd.property.TimeoutStopUSec": "uint64 123456789",
                "org.systemd.property.CollectMode":"'inactive-or-failed'"
        },

The above will set the following properties:

  • TimeoutStopSec to 2 minutes and 3 seconds;
  • CollectMode to "inactive-or-failed".

The values must be in the gvariant text format, as described in
gvariant documentation.

To find out which type systemd expects for a particular parameter, please
consult systemd sources.


Terminals (`docs/terminals.md`)

Terminals and Standard IO

Note that the default configuration of runc (foreground, new terminal) is
generally the best option for most users. This document exists to help explain
what the purpose of the different modes is, and to try to steer users away from
common mistakes and misunderstandings.

In general, most processes on Unix (and Unix-like) operating systems have 3
standard file descriptors provided at the start, collectively referred to as
"standard IO" (stdio):

  • 0: standard-in (stdin), the input stream into the process
  • 1: standard-out (stdout), the output stream from the process
  • 2: standard-error (stderr), the error stream from the process

When creating and running a container via runc, it is important to take care
to structure the stdio the new container's process receives. In some ways
containers are just regular processes, while in other ways they're an isolated
sub-partition of your machine (in a similar sense to a VM). This means that the
structure of IO is not as simple as with ordinary programs (which generally
just use the file descriptors you give them).

Other File Descriptors

Before we continue, it is important to note that processes can have more file
descriptors than just stdio. By default in runc no other file descriptors
will be passed to the spawned container process. If you wish to explicitly pass
file descriptors to the container you have to use the --preserve-fds option.
These ancillary file descriptors don't have any of the strange semantics
discussed further in this document (those only apply to stdio) -- they are
passed untouched by runc.

It should be noted that --preserve-fds does not take individual file
descriptors to preserve. Instead, it takes how many file descriptors (not
including stdio or LISTEN_FDS) should be passed to the container. In the
following example:

bash
runc run --preserve-fds 5 <container>

runc will pass the first 5 file descriptors (3, 4, 5, 6, and 7 --
assuming that LISTEN_FDS has not been configured) to the container.

In addition to --preserve-fds, LISTEN_FDS file descriptors are passed
automatically to allow for systemd-style socket activation. To extend the
above example:

bash
LISTEN_PID=$pid_of_runc LISTEN_FDS=3 runc run --preserve-fds 5 <container>

runc will now pass the first 8 file descriptors (and it will also pass
LISTEN_FDS=3 and LISTEN_PID=1 to the container). The first 3 (3, 4,
and 5) were passed due to LISTEN_FDS and the other 5 (6, 7, 8, 9,
and 10) were passed due to --preserve-fds. You should keep this in mind if
you use runc directly in something like a systemd unit file. To disable
this LISTEN_FDS-style passing just unset LISTEN_FDS.

Be very careful when passing file descriptors to a container process. Due
to some Linux kernel (mis)features, a container with access to certain types of
file descriptors (such as O_PATH descriptors) outside of the container's root
file system can use these to break out of the container's pivoted mount
namespace. This has resulted in CVEs in the past.

Terminal Modes

runc supports two distinct methods for passing stdio to the container's
primary process:

When first using runc these two modes will look incredibly similar, but this
can be quite deceptive as these different modes have quite different
characteristics.

By default, runc spec will create a configuration that will create a new
terminal (terminal: true). However, if the terminal: ... line is not
present in config.json then pass-through is the default.

In general we recommend using new terminal, because it means that tools like
sudo will work inside your container. But pass-through can be useful if you
know what you're doing, or if you're using runc as part of a non-interactive
pipeline.

New Terminal

In new terminal mode, runc will create a brand-new "console" (or more
precisely, a new pseudo-terminal using the container's namespaced
/dev/pts/ptmx) for your contained process to use as its stdio.

When you start a process in new terminal mode, runc will do the following:

  1. Create a new pseudo-terminal.
  2. Pass the slave end to the container's primary process as its stdio.
  3. Send the master end to a process to interact with the stdio for the
    container's primary process (
    details below).

It should be noted that since a new pseudo-terminal is being used for
communication with the container, some strange properties of pseudo-terminals
might surprise you. For instance, by default, all new pseudo-terminals
translate the byte '\n' to the sequence '\r\n' on both stdout and
stderr. In addition there are a whole range of ioctls(2) that can only
interact with pseudo-terminal stdio
.

NOTE: In new terminal mode, all three stdio file descriptors are the
same underlying file. The reason for this is to match how a shell's stdio
looks to a process (as well as remove race condition issues with having to
deal with multiple master pseudo-terminal file descriptors). However this
means that it is not really possible to uniquely distinguish between stdout
and stderr from the caller's perspective.

Issues

If you see an error like

text
open /dev/tty: no such device or address

from runc, it means it can't open a terminal (because there isn't one). This
can happen when stdin (and possibly also stdout and stderr) are redirected,
or in some environments that lack a tty (such as GitHub Actions runners).

The solution to this is to not use a terminal for the container, i.e. have
terminal: false in config.json. If the container really needs a terminal
(some programs require one), you can provide one, using one of the following
methods.

One way is to use ssh with the -tt flag. The second t forces a terminal
allocation even if there's no local one -- and so it is required when stdin is
not a terminal (some ssh implementations only look for a terminal on stdin).

Another way is to run runc under the script utility, like this

bash
script -e -c 'runc run <container>'

Pass-Through

If you have already set up some file handles that you wish your contained
process to use as its stdio, then you can ask runc to pass them through to
the contained process (this is not necessarily the same as --preserve-fds's
passing of file descriptors --
details below). As an example
(assuming that terminal: false is set in config.json):

bash
echo input | runc run some_container > /tmp/log.out 2> /tmp/log.err

Here the container's various stdio file descriptors will be substituted with
the following:

  • stdin will be sourced from the echo input pipeline.
  • stdout will be output into /tmp/log.out on the host.
  • stderr will be output into /tmp/log.err on the host.

It should be noted that the actual file handles seen inside the container may
be different based on the mode runc is being used in (for
instance, the file referenced by 1 could be /tmp/log.out directly or a pipe
which runc is using to buffer output, based on the mode). However the net
result will be the same in either case. In principle you could use the new
terminal mode
in a pipeline, but the difference will become
more clear when you are introduced to runc's detached mode.

`runc` Modes

runc itself runs in two modes:

You can use either terminal mode with either runc mode.
However, there are considerations that may indicate preference for one mode
over another. It should be noted that while two types of modes (terminal and
runc) are conceptually independent from each other, you should be aware of
the intricacies of which combination you are using.

In general we recommend using foreground because it's the most
straight-forward to use, with the only downside being that you will have a
long-running runc process. Detached mode is difficult to get right and
generally requires having your own stdio management.

Foreground

The default (and most straight-forward) mode of runc. In this mode, your
runc command remains in the foreground with the container process as a child.
All stdio is buffered through the foreground runc process (irrespective of
which terminal mode you are using). This is conceptually quite similar to
running a normal process interactively in a shell (and if you are using runc
in a shell interactively, this is what you should use).

Because the stdio will be buffered in this mode, some very important
peculiarities of this mode should be kept in mind:

  • With new terminal mode, the container will see a
    pseudo-terminal as its stdio (as you might expect). However, the stdio of
    the foreground runc process will remain the stdio that the process was
    started with -- and runc will copy all stdio between its stdio and the
    container's stdio. This means that while a new pseudo-terminal has been
    created, the foreground runc process manages it over the lifetime of the
    container.

  • With pass-through mode, the foreground runc's stdio is
    not passed to the container. Instead, the container's stdio is a set of
    pipes which are used to copy data between runc's stdio and the
    container's stdio. This means that the container never has direct access to
    host file descriptors (aside from the pipes created by the container runtime,
    but that shouldn't be an issue).

The main drawback of the foreground mode of operation is that it requires a
long-running foreground runc process. If you kill the foreground runc
process then you will no longer have access to the stdio of the container
(and in most cases this will result in the container dying abnormally due to
SIGPIPE or some other error). By extension this means that any bug in the
long-running foreground runc process (such as a memory leak) or a stray
OOM-kill sweep could result in your container being killed through no fault
of the user
. In addition, there is no way in foreground mode of passing a
file descriptor directly to the container process as its stdio (like
--preserve-fds does).

These shortcomings are obviously sub-optimal and are the reason that runc has
an additional mode called "detached mode".

Detached

In contrast to foreground mode, in detached mode there is no long-running
foreground runc process once the container has started. In fact, there is no
long-running runc process at all. However, this means that it is up to the
caller to handle the stdio after runc has set it up for you. In a shell
this means that the runc command will exit and control will return to the
shell, after the container has been set up.

You can run runc in detached mode in one of the following ways:

  • runc run -d ... which operates similar to runc run but is detached.
  • runc create followed by runc start which is the standard container
    lifecycle defined by the OCI runtime specification (runc create sets up the
    container completely, waiting for runc start to begin execution of user
    code).

The main use-case of detached mode is for higher-level tools that want to be
wrappers around runc. By running runc in detached mode, those tools have
far more control over the container's stdio without runc getting in the
way (most wrappers around runc like cri-o or containerd use detached mode
for this reason).

Unfortunately using detached mode is a bit more complicated and requires more
care than the foreground mode -- mainly because it is now up to the caller to
handle the stdio of the container.

Another complication is that the parent process is responsible for acting as
the subreaper for the container. In short, you need to call
prctl(PR_SET_CHILD_SUBREAPER, 1, ...) in the parent process and correctly
handle the implications of being a subreaper. Failing to do so may result in
zombie processes being accumulated on your host.

These tasks are usually performed by a dedicated (and minimal) monitor process
per-container. For the sake of comparison, other runtimes such as LXC do not
have an equivalent detached mode and instead integrate this monitor process
into the container runtime itself -- this has several tradeoffs, and runc has
opted to support delegating the monitoring responsibility to the parent process
through this detached mode.

Detached Pass-Through

In detached mode, pass-through actually does what it says on the tin -- the
stdio file descriptors of the runc process are passed through (untouched)
to the container's stdio. The purpose of this option is to allow a user to
set up stdio for a container themselves and then force runc to just use
their pre-prepared stdio (without any pseudo-terminal funny business). If
you don't see why this would be useful, don't use this option.

You must be incredibly careful when using detached pass-through (especially
in a shell).
The reason for this is that by using detached pass-through you
are passing host file descriptors to the container. In the case of a shell,
usually your stdio is going to be a pseudo-terminal (on your host). A
malicious container could take advantage of TTY-specific ioctls like
TIOCSTI to fake input into the host shell (remember that in detached
mode, control is returned to your shell and so the terminal you've given the
container is being read by a shell prompt).

There are also several other issues with running non-malicious containers in a
shell with detached pass-through (where you pass your shell's stdio to the
container):

  • Output from the container will be interleaved with output from your shell (in
    a non-deterministic way), without any real way of distinguishing from where a
    particular piece of output came from.

  • Any input to stdin will be non-deterministically split and given to either
    the container or the shell (because both are blocked on a read(2) of the
    same FIFO-style file descriptor).

They are all related to the fact that there is going to be a race when either
your host or the container tries to read from (or write to) stdio. This
problem is especially obvious when in a shell, where usually the terminal has
been put into raw mode (where each individual key-press should cause read(2)
to return).

NOTE: There is also currently a known problem where using
detached pass-through will result in the container hanging if the stdout or
stderr is a pipe (though this should be a temporary issue).

Detached New Terminal

When creating a new pseudo-terminal in detached mode, and fairly obvious
problem appears -- how do we use the new terminal that runc created? Unlike
in pass-through, runc has created a new set of file descriptors that need to
be used by something in order for container communication to work.

The way this problem is resolved is through the use of Unix domain sockets.
There is a feature of Unix sockets called SCM_RIGHTS which allows a file
descriptor to be sent through a Unix socket to a completely separate process
(which can then use that file descriptor as though they opened it). When using
runc in detached new terminal mode, this is how a user gets access to the
pseudo-terminal's master file descriptor.

To this end, there is a new option (which is required if you want to use runc
in detached new terminal mode): --console-socket. This option takes the path
to a Unix domain socket which runc will connect to and send the
pseudo-terminal master file descriptor down. The general process for getting
the pseudo-terminal master is as follows:

  1. Create a Unix domain socket at some path, $socket_path.
  2. Call runc run or runc create with the argument --console-socket $socket_path.
  3. Using recvmsg(2) retrieve the file descriptor sent using SCM_RIGHTS by
    runc.
  4. Now the manager can interact with the stdio of the container, using the
    retrieved pseudo-terminal master.

After runc exits, the only process with a copy of the pseudo-terminal master
file descriptor is whoever read the file descriptor from the socket.

NOTE: Currently runc doesn't support abstract socket addresses (due to
it not being possible to pass an argv with a null-byte as the first
character). In the future this may change, but currently you must use a valid
path name.

In order to help users make use of detached new terminal mode, we have provided
a Go implementation in the go-runc bindings, as
well as a simple client.


--- METRICS ---