docker

package
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 25, 2026 License: AGPL-3.0 Imports: 23 Imported by: 0

Documentation

Overview

Package docker is part of the Redoubt control plane. See CLAUDE.md for its role.

Index

Constants

View Source
const (
	LabelManaged = "redoubt.managed"
	LabelApp     = "redoubt.app"
	LabelKind    = "redoubt.kind"
	LabelRelease = "redoubt.release"
	LabelTest    = "redoubt.test"
)

Labels applied to every container the platform creates.

View Source
const (
	KindApp     = "app"
	KindAddon   = "addon"
	KindJob     = "job" // one-shot: backups, scans
	KindBuilder = "builder"
)

Kinds of containers the platform creates.

View Source
const (
	DefaultUser = "10001:10001"
	// JobUser is the platform uid: one-shot jobs (scans, dumps, restores) run as it so the 0700
	// work directories platformd creates under DataDir are writable to them and nobody else.
	JobUser            = "65532:65532"
	DefaultMemoryBytes = 512 << 20 // 512 MiB
	DefaultNanoCPUs    = 1_000_000_000
	DefaultPidsLimit   = 256
	// mode=1777: without it Docker creates the tmpfs root-owned 0755 and a non-root process
	// cannot even create a temp file — which would silently force apps back toward root.
	DefaultTmpfsOptions = "rw,noexec,nosuid,nodev,mode=1777"
	DefaultTmpSize      = "64m"
	DefaultRunSize      = "16m"
	DefaultAppArmor     = "docker-default"
	DefaultStopTimeout  = 15
	NoNewPrivileges     = "no-new-privileges:true"
)

Hardening defaults (CIS Docker Benchmark / OWASP Docker Top 10).

Variables

View Source
var AllowedCapAdd = map[string]bool{
	"NET_BIND_SERVICE": true,
	"CHOWN":            true,
	"SETUID":           true,
	"SETGID":           true,
}

AllowedCapAdd is the only set of capabilities an app profile may add back after cap_drop ALL.

View Source
var Allowlist = []Rule{
	rule(http.MethodGet, `/_ping`, "liveness + API version negotiation"),
	rule(http.MethodHead, `/_ping`, "liveness"),
	rule(http.MethodGet, `/version`, "API version negotiation"),
	rule(http.MethodGet, `/info`, "self-audit: daemon configuration"),
	rule(http.MethodGet, `/events`, "container lifecycle events"),
	rule(http.MethodGet, `/system/df`, "disk usage in the UI"),

	rule(http.MethodGet, `/containers/json`, "list managed containers"),
	rule(http.MethodPost, `/containers/create`, "create hardened containers (HostConfig built by this package)"),
	rule(http.MethodGet, `/containers/`+idPat+`/json`, "inspect (health, hardening verification)"),
	rule(http.MethodGet, `/containers/`+idPat+`/logs`, "deploy/app logs"),
	rule(http.MethodGet, `/containers/`+idPat+`/stats`, "resource usage in the UI"),
	rule(http.MethodPost, `/containers/`+idPat+`/start`, "start"),
	rule(http.MethodPost, `/containers/`+idPat+`/stop`, "stop"),
	rule(http.MethodPost, `/containers/`+idPat+`/restart`, "restart"),
	rule(http.MethodPost, `/containers/`+idPat+`/kill`, "kill after stop timeout"),
	rule(http.MethodPost, `/containers/`+idPat+`/wait`, "wait for one-shot jobs (backups, scans)"),
	rule(http.MethodDelete, `/containers/`+idPat, "remove"),

	rule(http.MethodGet, `/images/json`, "list images"),
	rule(http.MethodPost, `/images/create`, "pull"),
	rule(http.MethodPost, `/images/load`, "load BuildKit output tarball"),
	rule(http.MethodGet, `/images/`+imgPat+`/json`, "inspect image (USER, ports)"),
	rule(http.MethodGet, `/images/get`, "export image tarball for scanning (?names=)"),
	rule(http.MethodPost, `/images/`+imgPat+`/tag`, "tag releases"),
	rule(http.MethodDelete, `/images/`+imgPat, "prune old releases"),

	rule(http.MethodGet, `/networks`, "list networks"),
	rule(http.MethodGet, `/networks/`+idPat, "inspect network"),
	rule(http.MethodPost, `/networks/create`, "per-app networks"),
	rule(http.MethodPost, `/networks/`+idPat+`/connect`, "attach Traefik to an app network"),
	rule(http.MethodPost, `/networks/`+idPat+`/disconnect`, "detach Traefik"),
	rule(http.MethodDelete, `/networks/`+idPat, "remove app network"),

	rule(http.MethodGet, `/volumes`, "list volumes"),
	rule(http.MethodGet, `/volumes/`+idPat, "inspect volume"),
	rule(http.MethodPost, `/volumes/create`, "addon data volumes"),
	rule(http.MethodDelete, `/volumes/`+idPat, "remove addon data volumes"),
}

Allowlist is the exhaustive set of (method, path) pairs platformd may call.

Deliberately ABSENT (and covered by TestAllowlistDeniesDangerousEndpoints): exec (any form), attach, archive (file copy in/out), export, commit, build (we use a separate rootless BuildKit), update/rename/resize, plugins, swarm/nodes/services/tasks, secrets/configs, session, auth, distribution, grpc.

View Source
var ErrForbidden = errors.New("forbidden by hardening policy")

ErrForbidden is wrapped by every validation failure that would violate a golden rule.

View Source
var SeccompDefaultProfile string

SeccompDefaultProfile is Moby's default seccomp profile, shipped with the binary so that it is applied explicitly and verifiably (docker inspect shows it) on every host, including daemons whose configured default is "unconfined" (D-005). install/profiles/seccomp-default.json is a byte-identical copy for operators; TestSeccompProfileCopiesInSync keeps them in lock-step.

Functions

func Allowed

func Allowed(method, p string) bool

Allowed reports whether the (method, path) pair is permitted by the allow-list.

func AppContainerName

func AppContainerName(app, release string) string

AppContainerName returns the container name for a release of an app.

func AppNetworkName

func AppNetworkName(app string) string

AppNetworkName returns the per-app network name for an app.

func CopyLogs

func CopyLogs(w io.Writer, r io.Reader) error

CopyLogs demultiplexes a Docker log stream (stdout/stderr frames for non-TTY containers) into w, interleaving both streams. Callers redact before exposing the output.

func IsRootUser

func IsRootUser(u string) bool

IsRootUser reports whether a user string designates uid 0.

func NormalizePath

func NormalizePath(p string) (string, bool)

NormalizePath strips the optional /v1.xx API version prefix and cleans the path. It returns ok=false for paths that are not in canonical form (traversal, double slashes, trailing slash) so that callers reject them before matching.

func ValidateAppName

func ValidateAppName(name string) error

ValidateAppName reports whether name is a valid app name (lowercase DNS-label style).

func VerifyHardened

func VerifyHardened(insp container.InspectResponse) []string

VerifyHardened checks an inspected container against the golden-rule defaults. It is used by the integration tests AND by the self-audit command, so that what we test is what we ship.

Types

type Built

type Built struct {
	Name       string
	Config     *container.Config
	HostConfig *container.HostConfig
	Networking *network.NetworkingConfig
	Warnings   []Warning
}

Built is the result of Build: the exact structs passed to ContainerCreate plus the warnings produced by overrides.

type Call

type Call struct {
	Method string
	Path   string
}

Call is one recorded API request (method + path) — used by the allow-list drift test.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is the platform's typed wrapper around the Docker Engine API. Every call is a typed SDK call (golden rule 1) addressed to the socket-proxy (golden rule 2). It never shells out and never exposes the raw SDK client to callers.

func New

func New(host string, opts ...Option) (*Client, error)

New connects to the Docker Engine API at host, e.g. "tcp://socket-proxy:2375" in production or "unix:///var/run/docker.sock" in integration tests.

func (*Client) Close

func (c *Client) Close() error

Close releases the underlying connections.

func (*Client) ConnectContainer

func (c *Client) ConnectContainer(ctx context.Context, networkName, containerID string) error

ConnectContainer attaches a container to a network (idempotent).

func (*Client) Create

func (c *Client) Create(ctx context.Context, spec ContainerSpec) (string, []Warning, error)

Create builds the hardened configuration from spec and creates the container (not started).

func (*Client) CreateAndStart

func (c *Client) CreateAndStart(ctx context.Context, spec ContainerSpec) (string, []Warning, error)

CreateAndStart is Create followed by Start; on start failure the container is removed.

func (*Client) DisconnectContainer

func (c *Client) DisconnectContainer(ctx context.Context, networkName, containerID string) error

DisconnectContainer detaches a container from a network (idempotent).

func (*Client) EnsureNetwork

func (c *Client) EnsureNetwork(ctx context.Context, name string, internal bool) (string, error)

EnsureNetwork creates the named bridge network if it does not exist and returns its ID. internal networks have no external connectivity (addons that need no egress).

func (*Client) ImageExists

func (c *Client) ImageExists(ctx context.Context, ref string) (bool, error)

ImageExists reports whether the image is present locally.

func (*Client) ImageUser

func (c *Client) ImageUser(ctx context.Context, ref string) (string, error)

ImageUser returns the USER the image declares ("" if none).

func (*Client) Info

func (c *Client) Info(ctx context.Context) (system.Info, error)

Info returns daemon information (used by the self-audit).

func (*Client) Inspect

func (c *Client) Inspect(ctx context.Context, id string) (container.InspectResponse, error)

Inspect returns the full container description.

func (*Client) ListManaged

func (c *Client) ListManaged(ctx context.Context, app string) ([]container.Summary, error)

ListManaged lists containers created by the platform, optionally filtered to one app.

func (*Client) LoadImage

func (c *Client) LoadImage(ctx context.Context, tar io.Reader) error

LoadImage loads an image tarball (BuildKit docker exporter output).

func (*Client) Logs

func (c *Client) Logs(ctx context.Context, id string, follow bool, tail string) (io.ReadCloser, error)

Logs streams container logs. Callers must redact secrets before exposing the stream.

func (*Client) Ping

func (c *Client) Ping(ctx context.Context) error

Ping verifies connectivity to the daemon (through the proxy).

func (*Client) PullImage

func (c *Client) PullImage(ctx context.Context, ref string) error

PullImage pulls an image and drains the progress stream. Progress is discarded, not logged.

func (*Client) Remove

func (c *Client) Remove(ctx context.Context, id string, force bool) error

Remove deletes a container; missing containers are not an error.

func (*Client) RemoveImage

func (c *Client) RemoveImage(ctx context.Context, ref string) error

RemoveImage deletes an image reference; missing images are not an error.

func (*Client) RemoveNetwork

func (c *Client) RemoveNetwork(ctx context.Context, name string) error

RemoveNetwork deletes a network; missing networks are not an error.

func (*Client) RemoveVolume

func (c *Client) RemoveVolume(ctx context.Context, name string) error

RemoveVolume deletes a named volume; missing volumes are not an error.

func (*Client) SaveImage

func (c *Client) SaveImage(ctx context.Context, ref string) (io.ReadCloser, error)

SaveImage exports an image as a tarball (for scanning in an isolated container).

func (*Client) Start

func (c *Client) Start(ctx context.Context, id string) error

Start starts a created container.

func (*Client) Stop

func (c *Client) Stop(ctx context.Context, id string, timeout time.Duration) error

Stop stops a container with the given grace period.

func (*Client) TagImage

func (c *Client) TagImage(ctx context.Context, source, target string) error

TagImage tags source as target.

func (*Client) Wait

func (c *Client) Wait(ctx context.Context, id string) (int64, error)

Wait blocks until the container exits and returns its exit code.

func (*Client) WaitHealthy

func (c *Client) WaitHealthy(ctx context.Context, id string, timeout time.Duration) error

WaitHealthy polls until the container is running (and healthy if it has a healthcheck).

type ContainerSpec

type ContainerSpec struct {
	Name  string
	Image string
	Kind  string
	App   string // owning app (nameRe); used for the per-app network and labels
	// Release is a free-form release identifier stored in a label.
	Release string

	Cmd        []string
	Entrypoint []string
	Env        []string // secrets are injected here at runtime; never logged
	Labels     map[string]string
	WorkingDir string

	// User is "uid[:gid]". Empty selects DefaultUser. Root requires Overrides.AllowRoot.
	User string

	// Network is the per-app network name the container joins (required unless NoNetwork).
	Network string
	// NoNetwork attaches the container to the "none" network (one-shot jobs that need no I/O).
	NoNetwork bool
	// Aliases are extra DNS names on Network.
	Aliases []string

	// ExposedPort is documented for the router; it is never published on the host.
	ExposedPort int

	MemoryBytes int64
	NanoCPUs    int64
	PidsLimit   int64

	CapAdd []string // subset of AllowedCapAdd
	Mounts []Mount
	// ExtraTmpfs adds tmpfs mounts (path -> size) in addition to /tmp and /run.
	ExtraTmpfs map[string]string

	// OneShot containers (jobs) do not restart and are removed after they exit.
	OneShot bool

	// DataDir is the platform data directory; bind mounts must live under it.
	DataDir string
	// HostDataDir is where the daemon sees DataDir. platformd runs in a container with the data
	// directory bind-mounted at DataDir, but the daemon resolves bind sources on the host, so
	// sources under DataDir are rewritten to HostDataDir (empty or equal = no translation).
	HostDataDir string

	// AppArmorProfile overrides the AppArmor profile name (default docker-default).
	AppArmorProfile string

	// Healthcheck is passed through untouched (typed).
	Healthcheck *container.HealthConfig

	// Test marks containers created by the test-suite (label redoubt.test=1) so cleanup can
	// find them and nothing else on a developer machine is ever touched.
	Test bool

	Overrides Overrides
}

ContainerSpec is the platform's typed description of a container. It is the ONLY way platform code creates containers; Build() turns it into Docker SDK structs with every hardening default applied. There is no way to express Privileged, host networking, port publishing, or a Docker-socket mount.

func (ContainerSpec) Build

func (s ContainerSpec) Build() (*Built, error)

Build validates the spec and returns the hardened Docker SDK structures.

type Mount

type Mount struct {
	// Source is a named volume (Type=volume) or an absolute host path (Type=bind).
	Source   string
	Target   string
	ReadOnly bool
	Type     mount.Type
}

Mount is a validated volume/bind mount. Only named volumes and bind mounts under the platform data directory are permitted; the Docker socket and other host paths are refused.

type Option

type Option func(*options)

Option configures New.

func WithHTTPClient

func WithHTTPClient(hc *http.Client) Option

WithHTTPClient supplies a custom HTTP client (tests use it with a Recorder transport).

type Overrides

type Overrides struct {
	// AllowRoot permits User to be root (uid 0). Strongly discouraged.
	AllowRoot bool
	// WritableRootFS disables the read-only root filesystem.
	WritableRootFS bool
	// ExecutableTmp drops noexec from the /tmp tmpfs (some runtimes JIT into /tmp).
	ExecutableTmp bool
}

Overrides are the explicit, audited escape hatches. Every non-zero field weakens a default and produces a Warning that the caller MUST surface to the user and write to the audit log.

type Recorder

type Recorder struct {
	Base http.RoundTripper
	// contains filtered or unexported fields
}

Recorder is an http.RoundTripper that records every request the SDK issues.

func (*Recorder) Calls

func (r *Recorder) Calls() []Call

Calls returns a copy of the recorded calls.

func (*Recorder) RoundTrip

func (r *Recorder) RoundTrip(req *http.Request) (*http.Response, error)

RoundTrip implements http.RoundTripper.

type Rule

type Rule struct {
	Method string
	Path   *regexp.Regexp
	Why    string
}

Rule is one entry of the least-privilege Docker Engine API allow-list.

The allow-list is the single source of truth for what the control plane may ask the Docker daemon to do. It is enforced twice: by cmd/socket-proxy (the only process holding /var/run/docker.sock) and by the client-drift test in this package, which fails if the SDK wrapper ever issues a request the proxy would reject.

type Warning

type Warning struct {
	Code    string
	Message string
}

Warning describes one weakened default; it is also the audit event payload.

Directories

Path Synopsis
Package dockertest provides a minimal fake Docker Engine API for unit tests in other packages.
Package dockertest provides a minimal fake Docker Engine API for unit tests in other packages.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL