gaoler

module
v0.8.0 Latest Latest
Warning

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

Go to latest
Published: May 11, 2026 License: MIT

README

gaoler

CLI for hardened container sandboxes with a customisable image.

gaol /jāl/

A place of confinement, especially for minor offenses or provisional imprisonment; a jail.

gaoler is a command-line tool for creating and running hardened container sandboxes for running commands and workloads isolated from the main system, while providing an easy-to-use interface to manage these.

About

All containers are created from a base container image which is built and managed by the application.

Each container created with gaoler has the following settings to achieve hardening:

  • sudo is only permitted during init (--setup) and setup
  • The moby default seccomp profile
  • Removes Linux capabilities through cap_drop=ALL
  • Security option: no-new-privileges
  • Supported images assumes a non-root user
  • Limits process count
  • Default limits on CPU and memory

By default it mounts the current working directory into /home/<user>/workspaces/<dir> with support for additional workspaces and mounts.

Examples on configuration and image templates can be found in examples.

Concepts
Base image

By default new containers are created from a base image managed by the application. This base image can be modified without rebuilding by using setup (or flag --setup during init), and these changes can be saved in the base image with commit.

There are however several options on images for containers, see the section Image customisation for more details.

Containers

Each new container is created with the hardening described in the About section and are by default created by the applicaiton managed base image.

These containers can be further customised with environment variables, mounts and available resources through a configuration file and flags at startup.

The configuration file provides means to both set global container settings through the top-level container object and through profiles; which can be entirely independent from the top-level object, or they can inherit and override settings (including environment variables ad mounts) from the top-level configuration.

Workspaces

By default it mounts the current working directory into /home/<user>/workspaces/<dir> with support for additional workspaces.

When providing multiple workspaces with the flag --workspace, the target "main" working directory must be specified with the first --workspace flag.

If no workspace is wanted for a new container instance the flag --no-workspace can be provided.

Environment variables

Environment variables can be set through either configuration, or provided with flags at startup. If both are used the results will be merged with the flags taking precedence if there are any conflicts.

Depending on where the environment variables are set, various interpolation and evaluation is done on them.

Configuration:

container.env / profiles.profile-name.env:

A value can be set directly, or interpolation can be leveraged:

  • Value: {env:ENV_VAR}: Evaluate an environment variable on the host and set it to the environment variable in the container.
  • Value: {file:/path/to/file}: Reads a file and set the contents of the file to the environment variable in the container.

container.envFile / profiles.profile-name.envFile:

Reads a file with environment variables with key-value pairs where the key is the environment variable name, and the value is the value of the environment variable.

The result is merged into container.env / profiles.profile-name.env.

If a value is not set for a key it will attempt to read the environment variable from the host.

Format for file:

ENV_VAR_1=value1
ENV_VAR_2=value2
ENV_VAR_3

Flags:

Flag --env / -e can use used like so:

  • --env ENV_VAR=value: Set the environment variable to the provided value
  • --env ENV_VAR: If an environment variable on the local host has that name it will be provided to the container.
Mounts

Mounts can be set through either configuration, or provided with flags at startup. If both are used the results will be merged with the flags taking precedence if there are any conflicts.

Mounts set in configuration supports various ways of interpolation.

Destination paths with suffix :ro will be set to read-only.

Configuration

container.mounts / profiles.profile-name.mounts

These are set as a key and a value where the key is the source path on the local host, and the value is the destination path in the container.

Interpolation is supported through:

  • ~: The home directory. When specified in the source path it resolves to the home directory on the host, and if set in the destination path it resolves to the home directory in the container.
  • {HOME}: The home directory. When specified in the source path it resolves to the home directory on the host, and if set in the destination path it resolves to the home directory in the container.
  • {USER}: The user. When specified in the source path it resolves to the user on the host, and if set in the destination path it resolves to the user in the container.

Flags

Flag --mounts / -m can use used like so:

  • /local/path:/container/path
  • /local/path:/container/path:ro (set to read-only)
Profiles

Profiles provide an additional way to customise containers for different scenarios and/or setups.

Profiles work together with, and as an extension to the global container settings.

Profiles can inherit resource limits (if they are not set), and mounts and environment variables with the field inherit: true

Further details can be found at the profiles section of the configuration section.

Install

[!NOTE] An install script will be available at a later time.

Prerequisites:

  • Docker Engine
Linux

Go to Releases and download the latest release for Linux:

# This example uses architecture amd64 as an example, if you are using arm64, change it.
curl -sLO https://codeberg.org/KarlGW/gaoler/releases/download/v0.8.0/gaoler-0.8.0-linux-amd64.tar.gz
mkdir -p ~/.local/bin
tar -C ~/.local/bin -xvf gaoler-0.8.0-linux-amd64.tar.gz && rm gaoler-0.8.0-linux-amd64.tar.gz

[!NOTE] Make sure ~/.local/bin is set to $PATH.

macOS

Go to Releases and download the latest release for macOS:

# This example uses architecture arm64 as an example, if you are using amd64, change it.
curl -sLO https://codeberg.org/KarlGW/gaoler/releases/download/v0.8.0/gaoler-0.8.0-darwin-arm64.tar.gz
mkdir -p ~/.local/bin
tar -C ~/.local/bin -xvf gaoler-0.8.0-darwin-arm64.tar.gz && rm gaoler-0.8.0-darwin-arm64.tar.gz

[!NOTE] Make sure ~/.local/bin is set to $PATH.

Windows

Go to Releases and download the latest release for Windows.

Unzip it into a directory/add directory to $PATH.

go install
go install codeberg.org/KarlGW/gaoler/cmd/gaoler@latest
Setup autocompletion

To enable auto/tab completion for gaoler follow the steps below depending on shell.

Bash

Current session:

gaoler source <(gaoler completion bash)

For all sessions:

echo -e "\n# gaoler\nsource <(gaoler completion bash)" >> ~/.bashrc
Zsh

Current session:

source <(gaoler completion zsh)

For all sessions:

echo -e "\n# gaoler\nsource <(gaoler completion zsh)" >> ~/.zshrc
PowerShell

First create the autocompletion script:

./gaoler completion powershell >> "$(Split-Path $PROFILE)/gaoler.ps1"

Current session:

& "$(Split-Path $PROFILE)/gaoler.ps1"

For all sessions:

"& $(Split-Path $PROFILE)/gaoler.ps1" >> $PROFILE

Quick start

  1. Initialise and build the container:

    gaoler init
    
  2. Run a container:

    gaoler run
    

This will start a container that mounts the current working directory into the path /home/<user>/workspaces/<dir>.

[!TIP] Detach from a container with CTRL+P, CTRL+Q.

[!NOTE] Each subsequent call to run will create a new container named: gaoler-<short-uuid>.

For additional information on use run:

gaoler --help

And:

gaoler <command> --help
Coding agents

As gaoler provides hardened containers it's ideal to use together with coding agents. Check out Coding agents over at the example section for more detailed information.

Configuration

The configuration file is located at ~/.gaoler/config.json.

This can be used to configure container image settings and container configuration that should apply to all new containers.

Many of these can be set at run time when handling containers.

Supported settings (default settings shown):

{
  "image": {
    "rootCommands": [],
    "userCommands": [],
    "template": ""
  },
  "container": {
    "env": {},
    "envFile": "",
    "mounts": {},
    "cpus": 1,
    "memory": "2g",
    "maxPids": 100
  },
  "profiles": {},
  "endpoint": ""
}

[!NOTE] endpoint is automatically populated at first run. If connection to the Docker host cannot be done, this needs manual configuration.

Example configuration:

{
  "image": {
    "rootCommands": [
      "RUN <command>"
    ],
    "userCommands": [
      "RUN <command>"
    ],
  },
  "container": {
    "env": {
      "ENV_VAR": "value"
    },
    "envFile": "/path/to/env-file",
    "mounts": {
      "/path/to/local/dir": "/path/to/container/dir"
    },
    "cpus": 1,
    "memory": "2g",
    "maxPids": 100
  },
  "profiles": {
    "profile-name": {
      "env": {
        "ENV_VAR": "value"
      },
      "envFile": "/path/to/env-file",
      "mounts": {
        "/path/to/local/dir": "/path/to/container/dir"
      },
      "cpus": 2,
      "memory": "4g",
      "maxPids": 100,
      "inherit": false
    }
  },
  "endpoint": "unix:///var/run/docker.sock"
}

image

Contains configuration for the container image.

image.rootCommands: Dockerfile instructions run as root.

Example:

[
  "RUN <command>",
  "RUN <command>"
]

image.userCommands: Dockerfile instructions run as user.

Example:

[
  "RUN <command>",
  "RUN <command>"
]

image.template: Path to a custom template. Supports ~, {HOME} and {USER} to resolve user home directory and username.

container

Contains configuration for each instance of a running container.

container.env: Defined as key-value pairs where the key is the environment variable name, and the value is the value of the environment variable.

Supports interpolation through:

  • Value: {env:ENV_VAR}: Evaluate an environment variable on the host and set it to the environment variable in the container.
  • Value: {file:/path/to/file}: Reads a file and set the contents of the file to the environment variable in the container.

container.envFile: Reads a file with environment variables with key-value pairs where the key is the environment variable name, and the value is the value of the environment variable.

The result is merged into container.env.

If a value is not set for a key it will attempt to read the environment variable from the host.

Format for file:

ENV_VAR_1=value1
ENV_VAR_2=value2
ENV_VAR_3

Lines with prefix # or // are ignored.

container.mounts: Defined as key-value pairs where the key is he source (local) path, and the value is the destination (container) path.

Example:

{
  "/path/to/local/dir1": "/path/to/container/dir1",
  "/path/to/local/dir2": "/path/to/container/dir2:ro"
}

Supports ~, {HOME} and {USER} to resolve user home directory and username. The local path resolves to the local home directory. The destination path resolves to the container home directory.

The suffix :ro makes the mount read-only.

container.cpus: Number of CPUs available to the container.

container.memory: Memory limit of the contaier. Is specified as a string. As an example 1 GiB would be specified as 1g, and 2 GiB would be specified as 2g.

container.maxPids: Number of maximum process IDs (processes) that can run in the container.

profiles

Container configuration profiles. Provides possibilities to setup multiple container configurations that can be through providing the flag --profile to run.

The key is the profile name, and the value is the container configuration.

Example:

{
  "profiles": {
    "profile-name": {
      "env": {
        "ENV_VAR": "value"
      },
      "envFile": "/path/to/env-file",
      "mounts": {
        "/path/to/local/dir": "/path/to/container/dir"
      },
      "cpus": 1,
      "memory": "2g",
      "maxPids": 100
      "inherit": false,
    }
  }
}

profiles.profile-name

Has the same fields as container with the added field inherit.

profiles.profile-name.inherit:

Set to true to inherit settings from the global container configuration. Values set in the profile will take precedence. Undefined inherit is the same as false.

  • env from global container configuration will be merged into the ones set on the profile
  • mounts from global container configuration will be merged into the ones set on the profile.
  • image will be inherited from the global configuration if NOT set in the profile
  • cpus will be inherited from the global configuration if NOT set in the profile
  • memory will be inherited from the global configuration if NOT set in the profile
  • maxPids will be inherited from the global configuration if NOT set in the profile

endpoint

Docker host endpoint (unix socket). Automatically populated at first run. If connection to the Docker host cannot be done, this needs manual configuration.

Command reference

This section provides information about the available commands. For further details run gaoler <command> --help.

init

Initialise and build the container image. Is required before running any container. With flag --setup an interactive shell session is started after image build has completed to allow for additional setup tasks. See setup for additional information.

Supports to build and restore a user home directory with the --restore flag.

[!NOTE] Not required if an image have been built with docker build and labeled with gaoler=<version>.

setup

Enter an interactive setup session for the container image. To persist changes done during this session run commit when done.

commit

Commit changes performed during setup or init(with --setup flag).

backup

Backup user home directory. Default backup location is ~/.gaoler/backup

restore

Restore user home directory. Uses latest backup by default.

copy

Copy files from/to a container.

config

Not implemented

run

Create and start a container. Enters an interactive session unless flag --detach is specified. To detach and keep the container running without a running program provide flag --tty.

Provides many customisation options, see run --help for more.

Detach from active session with CTRL+P, CTRL+Q.

exec

Run a command in a new process in an already running container.

Detach from active session with CTRL+P, CTRL+Q.

attach

Attach to a container after a detach, either from run/exec with flag --detach, or after providing CTRL+P, CTRL+Q.

list

List containers managed by gaoler.

remove

Remove containers managed by gaoler.

image
image list

List images managed by gaoler.

image remove

Remove images managed by gaoler. Removing an image will remove any containers, if any, associated with that image.

Image customisation

The container image can be customised in multiple ways:

  • At image build
  • At init
  • With setup
  • A template
  • An external image labeled with gaoler

[!NOTE] sudo can only be used during init or setup.

Customise at image build

Customising the image during build keeps the image customisation consistent between builds and keeps the amount of image layers down.

This can be achieved with either setting rootCommands and userCommands in ~/.gaoler/config.json or provifing the flags --root-commands and --user-commands together with init.

What this does is to add instructions in the Dockefile used for the image.

The root commands are run as root after the initial base setup is done.

The user commands are run as the configured user after it has been created and set as the current user.

Example:

FROM ubuntu:24.04

RUN apt-get update \
  && apt-get install -y \
  curl \
  git \
  sudo \
  vim

# Root commands will be added here.
# ... ...

RUN useradd \
  --uid 10001 \
  --user-group \
  --create-home \
  --shell /bin/bash \
  <user> \
  && usermod -a -G sudo <user> \
  && cp /etc/sudoers /etc/sudoers.tmp \
  && echo "<user> ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.tmp \
  && mv /etc/sudoers.tmp /etc/sudoers

USER <user>

WORKDIR /home/<user>

# User commands will be added here.
# ... ...
Customise at init

During the default behaviour of init an interactive session to a setup container will be started. This provides an opportunity to perform administrative tasks and other setup.

Afterwards run commit to commit the changes to the container image.

Customise with setup

Running setup will create a setup container. As with the interactive init, administrative tasks and setup can be done.

Afterwards run commit to commit the changes to the container image.

Custom template

A custom template can be provided with either the configuration file or a command line flag.

Example template:

FROM {{.BaseImage}}

# Commands
{{range .RootCommands}}
{{.}}
{{end}}
{{template "useradd" .}}
{{range .UserCommands}}
{{.}}
{{end}}
{{- end -}}
External image

An external image (built with docker build as an example) needs the following:

  • A user with sudo privileges
  • Be labeled with gaoler:<version> (if gaoler should be able to manage it)

TODO

  • Support for other "built-in" base images than ubuntu
  • Support for custom username in container
  • Support for no network
    • Proxy side car for HTTP/HTTPS
    • Proxy-command side car for commands

Directories

Path Synopsis
cmd
gaoler command
internal
cli
container
Package container provides a means to work with conainers through the moby client.
Package container provides a means to work with conainers through the moby client.

Jump to

Keyboard shortcuts

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