diff options
Diffstat (limited to '2026')
3 files changed, 648 insertions, 0 deletions
diff --git a/2026/apple-container-machine-machines/Dockerfile.recon-machine b/2026/apple-container-machine-machines/Dockerfile.recon-machine new file mode 100644 index 0000000..79897ff --- /dev/null +++ b/2026/apple-container-machine-machines/Dockerfile.recon-machine @@ -0,0 +1,72 @@ +FROM ubuntu:24.04 + +ENV container container + +# Base system + networking +RUN apt-get update && \ + apt-get install -y \ + dbus systemd openssh-server \ + curl wget ca-certificates \ + sudo iproute2 iputils-ping net-tools \ + dnsutils whois traceroute mtr-tiny \ + && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Recon CLI tooling +RUN apt-get update && \ + apt-get install -y \ + nmap \ + netcat-openbsd \ + nikto \ + sqlmap \ + hydra \ + jq \ + python3 python3-pip python3-venv \ + dnsmap \ + dnsrecon \ + dnswalk \ + dirb \ + wfuzz \ + sslscan \ + testssl.sh \ + whatweb \ + smbclient \ + ldap-utils \ + && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Go for installing Go-based tools +RUN apt-get update && \ + apt-get install -y golang-go && \ + apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install Go-based recon tools +RUN go install github.com/projectdiscovery/subfinder/v2/cmd/subfinder@latest && \ + go install github.com/projectdiscovery/httpx/cmd/httpx@latest && \ + go install github.com/projectdiscovery/naabu/v2/cmd/naabu@latest && \ + go install github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest && \ + go install github.com/tomnomnom/httprobe@latest && \ + go install github.com/tomnomnom/gf@latest && \ + go install github.com/tomnomnom/waybackurls@latest && \ + go install github.com/tomnomnom/assetfinder@latest && \ + go install github.com/ffuf/ffuf/v2@latest && \ + go install github.com/OJ/gobuster/v3@latest + +# Copy go-installed binaries to system PATH and add GOPATH/bin to profile +RUN cp /root/go/bin/* /usr/local/bin/ 2>/dev/null; true +RUN echo 'export PATH=$PATH:/root/go/bin' > /etc/profile.d/gopath.sh && chmod +x /etc/profile.d/gopath.sh + +# Init setup for container machine +RUN >/etc/machine-id && >/var/lib/dbus/machine-id + +RUN systemctl set-default multi-user.target && \ + systemctl mask \ + dev-hugepages.mount \ + sys-fs-fuse-connections.mount \ + systemd-update-utmp.service \ + systemd-tmpfiles-setup.service \ + console-getty.service && \ + systemctl disable \ + networkd-dispatcher.service + +# SSH config for remote access +RUN sed -i -e 's/^AcceptEnv LANG LC_\*$/#AcceptEnv LANG LC_*/' /etc/ssh/sshd_config && \ + sed -i 's/^#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config diff --git a/2026/apple-container-machine-machines/Dockerfile.ubuntu-systemd-admin b/2026/apple-container-machine-machines/Dockerfile.ubuntu-systemd-admin new file mode 100644 index 0000000..4f5ebaf --- /dev/null +++ b/2026/apple-container-machine-machines/Dockerfile.ubuntu-systemd-admin @@ -0,0 +1,51 @@ +FROM ubuntu:24.04 + +ENV container container + +# Base system + networking +RUN apt-get update && \ + apt-get install -y \ + dbus systemd openssh-server \ + curl ca-certificates \ + sudo iproute2 iputils-ping net-tools \ + && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Systemd admin tooling +RUN apt-get update && \ + apt-get install -y \ + systemd-container \ + tmux \ + fzf \ + jq \ + ripgrep \ + bash-completion \ + && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Init setup for container machine +RUN >/etc/machine-id && >/var/lib/dbus/machine-id + +RUN systemctl set-default multi-user.target && \ + systemctl mask \ + dev-hugepages.mount \ + sys-fs-fuse-connections.mount \ + systemd-update-utmp.service \ + systemd-tmpfiles-setup.service \ + console-getty.service && \ + systemctl disable \ + networkd-dispatcher.service + +# SSH config for remote access +RUN sed -i -e 's/^AcceptEnv LANG LC_\*$/#AcceptEnv LANG LC_*/' /etc/ssh/sshd_config && \ + sed -i 's/^#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config + +# Admin directory structure +RUN mkdir -p /etc/systemd-admin /etc/skel/.ssh/controlmasters + +# SSH client config template for new users (applies to host user via home mount) +COPY ssh_config /etc/skel/.ssh/config +RUN chmod 600 /etc/skel/.ssh/config + +# Copy hosts file and profile helpers +COPY hosts /etc/systemd-admin/hosts +COPY systemd-admin.sh /etc/profile.d/systemd-admin.sh +RUN chmod 644 /etc/systemd-admin/hosts /etc/profile.d/systemd-admin.sh diff --git a/2026/apple-container-machine-machines/apple-container-machine-skill.md b/2026/apple-container-machine-machines/apple-container-machine-skill.md new file mode 100644 index 0000000..f0fd19b --- /dev/null +++ b/2026/apple-container-machine-machines/apple-container-machine-skill.md @@ -0,0 +1,525 @@ +--- +name: apple-container-machine +description: > + Use this skill when working with Apple's `container machine` subcommand on + Apple Silicon Macs — creating persistent Linux environments, building custom + machine images, managing lifecycle, setting up VSCode Remote-SSH development, + or configuring host integration (home-dir mount, SSH, DNS). Triggers on + "container machine", "container m", "machine create", "machine run", + "apple container machine", "container machine image", "m run", or any + request involving Apple's container tooling for persistent Linux environments. +--- + +# Apple Container Machine Skill + +## What This Is + +`container machine` (alias `m`) is a subcommand of Apple's [container](https://github.com/apple/container) tool — an open-source CLI that runs Linux containers as lightweight virtual machines on Apple Silicon Macs (macOS 26+). Unlike standard `container run` which models an application container, `container machine` models a **persistent Linux workstation environment**: it runs the image's init system (`/sbin/init`), maps your macOS user account and home directory into the VM, and survives stop/start cycles. + +**Key differences from `container run`:** +- Runs `/sbin/init` (supports `systemd`, `openrc`, etc.) for long-running services +- Auto-maps host username, UID, GID, and `$HOME` into the VM +- Persistent filesystem across stop/start — data survives +- Resources (CPU, memory) configurable at create time and resizeable later +- One machine per target distro — test the same app under Alpine, Ubuntu, Debian + +## Prerequisites + +- Apple Silicon Mac (M1+) +- macOS 26 recommended (macOS 15 has limitations: no container-to-container networking, no `container network` commands) +- `container` tool installed and `container system start` has been run +- Linux kernel installed (`container system kernel set --recommended`) + +## Quickstart + +```bash +# Create a container machine from an OCI image +container machine create alpine:3.22 --name dev + +# Run a command (boots automatically if stopped) +container machine run -n dev whoami # your host username, not root +container machine run -n dev pwd # your Mac home directory, mounted in + +# Open an interactive shell +container machine run -n dev # login shell as your host user + +# List all machines +container machine ls + +# Stop and delete +container machine stop dev +container machine rm dev +``` + +The alias `m` works everywhere `machine` does: `m ls`, `m run -n dev`, `m stop dev`. + +## Container Machine Lifecycle + +``` +pull image → create → boot → run commands / services → stop → delete +``` + +### Create + +```bash +container machine create <image> --name <name> [--cpus N] [--memory N] [--home-mount rw|ro|none] +``` + +The image must contain `/sbin/init`. See "Image Requirements" below. + +- `--no-boot` — create without booting +- `--set-default` — also set as the default machine +- `--cpus` — virtual CPUs (default: half of host count) +- `--memory` — RAM (default: half of host memory, e.g., `8G`, `16G`) +- `--home-mount` — home directory access: `rw` (default, read-write), `ro` (read-only), `none` (no mount) + +### Boot + +A machine boots automatically on first `container machine run` or when `run` is invoked on a stopped machine. No separate boot command is needed. + +### Run Commands + +```bash +container machine run -n <name> [command...] # run a single command and exit +container machine run -n <name> # interactive login shell +container machine run -n <name> -- <command...> # pass args after -- +container machine run -n <name> --root # run as root instead of host user +container machine run -n <name> --detach <command> # run in background +``` + +**Quoting limitation.** `container machine run` wraps the command in a shell layer that re-interprets quoting. Complex shell constructs — `&&` chaining, heredocs (`<<`), pipes to the command, mixed quote styles — are unreliable. Use simple single commands; for multi-step work, write a script on the mounted home dir and invoke it: + +```bash +# Reliable: simple command +container machine run -n sdamin -- cat /etc/os-release + +# Unreliable: this will likely mangle quoting +container machine run -n sdamin -- sh -c "systemctl status nginx && journalctl -u nginx -n 5" + +# Reliable workaround: source the script from the mounted home +container machine run -n sdamin -- bash ~/projects/scripts/check.sh +``` + +Process flags: `-e KEY=value`, `--env-file`, `-u user`, `--uid`, `--gid`, `-w /workdir`, `-i` (interactive), `-t` (tty). + +### Inspect + +```bash +container machine inspect <name> # JSON detail +container machine ls # table: name, IP, cpus, memory, disk, state +container machine logs <name> # container machine stdio logs +container machine logs --boot <name> # VM boot log +``` + +### Stop + +```bash +container machine stop <name> # graceful stop +``` + +### Configure (resize resources) + +```bash +container machine set -n <name> cpus=4 memory=8G +container machine stop <name> +container machine run -n <name> # restart for changes to take effect +``` + +### Set Default + +```bash +container machine set-default <name> # drop the -n flag for subsequent commands +``` + +### Delete + +```bash +container machine stop <name> +container machine rm <name> # removes machine AND persistent storage +``` + +If the deleted machine was the default, set a new one: `container machine set-default <new-name>`. + +## Image Requirements + +Container machines require OCI images that include `/sbin/init`. The machine runs the image's init system as PID 1 on boot. + +### Ubuntu 24.04 + systemd (canonical example) + +This Dockerfile builds a machine image with systemd and common tools: + +```dockerfile +FROM ubuntu:24.04 + +ENV container container + +RUN apt-get update && \ + apt-get install -y \ + dbus systemd openssh-server net-tools iproute2 iputils-ping curl wget vim-tiny man sudo && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* && \ + yes | unminimize + +RUN >/etc/machine-id +RUN >/var/lib/dbus/machine-id + +RUN systemctl set-default multi-user.target +RUN systemctl mask \ + dev-hugepages.mount \ + sys-fs-fuse-connections.mount \ + systemd-update-utmp.service \ + systemd-tmpfiles-setup.service \ + console-getty.service +RUN systemctl disable \ + networkd-dispatcher.service + +RUN sed -i -e 's/^AcceptEnv LANG LC_\*$/#AcceptEnv LANG LC_*/' /etc/ssh/sshd_config +``` + +Build and create: + +```bash +container build -t local/ubuntu-machine:latest -f Dockerfile . +container machine create local/ubuntu-machine:latest --name ubuntu +``` + +### systemd on other distros + +The same pattern applies to any systemd-based distro (Debian, Fedora, Rocky Linux): +- Set `ENV container container` +- Install `dbus` and `systemd` +- Create empty `/etc/machine-id` and `/var/lib/dbus/machine-id` +- Set `multi-user.target` as default +- Mask unnecessary services + +### init systems other than systemd + +Any Linux image with `/sbin/init` works. For Alpine (busybox-init) or distros with OpenRC, the init system handles PID 1 responsibilities. + +### First-boot provisioner + +By default, `container` runs a built-in setup script on first boot to create the user matching your host account. To use your own setup instead, add an executable script at `/etc/machine/create-user.sh` to the image. It runs once, as root, on first boot, with these environment variables: + +- `CONTAINER_GID` — group ID +- `CONTAINER_HOME` — home directory path +- `CONTAINER_MACHINE_ID` — machine UUID +- `CONTAINER_UID` — user ID +- `CONTAINER_USER` — username + +## Commands Reference + +`m` is the alias for `machine` — both forms work everywhere. + +| Command | Description | +|---------|-------------| +| `container machine create <image>` | Create and boot a machine. Flags: `--name`, `--cpus`, `--memory`, `--home-mount`, `--no-boot`, `--set-default`, `--scheme`, `--progress` | +| `container machine run` | Run a command or open a shell in a machine (boots if stopped). Flags: `-n <name>`, `-d` (detach), `--root`, process flags (`-e`, `-u`, `-w`, `-i`, `-t`, `--gid`, `--uid`, `--env-file`) | +| `container machine ls` | List machines. Flags: `--format json\|table`, `-q` | +| `container machine inspect <name>` | JSON detail of a machine | +| `container machine set <key=value...>` | Update config. Flags: `-n <name>`. Valid keys: `cpus`, `memory`, `home-mount`. Requires stop/start. | +| `container machine set-default <name>` | Set the default machine | +| `container machine stop <name>` | Stop a running machine | +| `container machine logs <name>` | Show logs. Flags: `--boot`, `-f` (follow), `-n N` | +| `container machine rm <name>` | Delete machine and persistent storage | + +## Host Integration + +### Home directory mount + +The container machine automatically mounts your macOS home directory inside the VM at `/Users/<username>`. Your repos, dotfiles, and SSH keys are available from both macOS and the Linux environment. + +```bash +container machine create ... --home-mount rw # read-write (default) +container machine create ... --home-mount ro # read-only +container machine create ... --home-mount none # no mount +``` + +- The host user is automatically created in the machine with matching UID/GID +- Edit on macOS with your native tools; compile and run in the Linux VM +- Profilers, screenshot tools, and debuggers on macOS see the same files + +**Important: `$HOME` path mismatch.** The machine sets `$HOME` to `/home/<username>` inside the VM, but the actual mounted home is at `/Users/<username>`. Tools that resolve `~` relative to `$HOME` (SSH key lookup, config file discovery) may look in the wrong place. This most commonly breaks SSH identity file resolution. Workaround: + +```bash +# Symlink the VM home .ssh to the mounted macOS .ssh +container machine run -n <name> --root ln -s /Users/<username>/.ssh /home/<username>/.ssh +``` + +Or use absolute paths when referencing files on the mounted home. + +### SSH agent forwarding + +Add an SSH config entry so the machine can access your SSH agent: + +```bash +cat >> ~/.ssh/config <<EOT + +Host *.machine + ForwardAgent yes + UserKnownHostsFile /dev/null +EOT +``` + +### DNS domain setup + +Create a local DNS domain so you can reach machines by hostname: + +```bash +sudo container system dns create machine +``` + +After this, `ping <machine-name>.machine` resolves to the machine's IP: + +```bash +ping -c 1 ubuntu.machine +``` + +To remove the domain later: + +```bash +sudo container system dns delete machine +``` + +### Passwordless sudo inside a machine + +Services started via `systemctl` inside the machine run as root by default. For interactive sudo access: + +```bash +container machine run -n <name> sudo passwd $(whoami) +# Enter a password, then use sudo normally in shells +``` + +## VSCode Remote-SSH Development + +This workflow lets you use Visual Studio Code with the Remote-SSH extension to develop code stored on your Mac, compiled and run inside the container machine. + +### Setup + +1. Create a machine with systemd and SSH: + +```dockerfile +FROM ubuntu:24.04 + +ENV container container + +RUN apt-get update && \ + apt-get install -y \ + dbus systemd openssh-server sudo curl wget git && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +RUN >/etc/machine-id +RUN >/var/lib/dbus/machine-id + +RUN systemctl set-default multi-user.target +RUN systemctl mask \ + dev-hugepages.mount \ + sys-fs-fuse-connections.mount \ + systemd-update-utmp.service \ + console-getty.service +``` + +2. Build and create: + +```bash +container build -t ubuntu-machine:latest . +container machine create --set-default --name ubuntu ubuntu-machine:latest +``` + +3. Set up DNS: + +```bash +sudo container system dns create machine +``` + +4. Add SSH config: + +```bash +cat >> ~/.ssh/config <<EOT + +Host ubuntu.machine + HostName ubuntu.machine + ForwardAgent yes + UserKnownHostsFile /dev/null +EOT +``` + +5. Set a password for SSH: + +```bash +container machine run -it sudo passwd $(whoami) +``` + +### Connect + +1. In VSCode, press Cmd-Shift-P and run **Remote-SSH: Connect to Host** +2. Select `ubuntu.machine` +3. Accept the SSH fingerprint +4. Enter the password you configured +5. In the new window, install any language extensions (Swift, Python, Rust, etc.) in the remote context + +Your macOS home directory is mounted at `/Users/<username>` inside the machine — all your repos are available. + +### Debugging + +Set breakpoints in VSCode, run the application inside the machine, and debug normally. The home-dir mount means compiled artifacts are visible to both the machine and macOS tools. + +### Clean up + +```bash +container machine stop ubuntu +container machine rm ubuntu +container image rm ubuntu-machine:latest +sudo container system dns delete machine +# Remove the SSH config entry from ~/.ssh/config +``` + +## Resource Configuration + +### At create time + +```bash +container machine create alpine:3.22 --name dev --cpus 4 --memory 8G +``` + +- Default CPUs: half of host count +- Default memory: half of host memory +- Memory format: `512M`, `2G`, `8G`, etc. (1 MiB granularity) + +### Post-creation resize + +```bash +container machine set -n dev cpus=8 memory=16G +container machine stop dev +container machine run -n dev -- nproc # verify: 8 +``` + +Changes only take effect after a stop/start cycle. + +### Check current resources + +```bash +container machine inspect dev | jq '.cpus, .memoryInBytes' +container machine ls # table shows CPUS and MEMORY +``` + +## Multi-Distro Workflows + +Create one container machine per target distribution. Each machine mounts the same `$HOME`, so your repos and dotfiles are available everywhere: + +```bash +container machine create alpine:3.22 --name dev-alpine +container machine create ubuntu:24.04 --name dev-ubuntu # uses /sbin/init from systemd +container machine create debian:latest --name dev-debian +``` + +```bash +container machine run -n dev-alpine apk add build-base +container machine run -n dev-ubuntu apt-get install build-essential +container machine run -n dev-debian apt-get install build-essential +``` + +Each machine runs its own init system under its own Linux VM, fully isolated. Test your application under different distributions and init systems from the same source tree. + +## Troubleshooting + +### Machine fails to boot + +Ensure a Linux kernel is installed: +```bash +container system kernel set --recommended +``` + +Check boot logs: +```bash +container machine logs --boot <name> +``` + +### DNS not resolving (`<name>.machine` unknown) + +Verify the DNS domain was created: +```bash +cat /etc/resolver/machine +sudo container system dns create machine +``` + +Verify the machine is running and has an IP: +```bash +container machine ls +``` + +### SSH connection refused + +Ensure SSH is installed and running inside the machine: +```bash +container machine run -n <name> sudo systemctl status ssh +container machine run -n <name> sudo systemctl enable --now ssh +``` + +On images without systemd, start sshd explicitly: +```bash +container machine run -n <name> sudo /usr/sbin/sshd +``` + +Check SSH config: +```bash +ssh -v ubuntu.machine +``` + +### Home-mount permission errors + +If the machine uses `--home-mount ro`, writes to `$HOME` will fail. Switch to `rw`: + +```bash +container machine set -n <name> home-mount=rw +container machine stop <name> +container machine run -n <name> +``` + +### macOS 15 limitations + +On macOS 15, the following do not work: +- Container-to-container networking over the virtual network +- `container network` commands +- `--network` flag on `container run`/`container create` + +Container machines still work but are limited to host-only networking. + +### Resource not updating after `container machine set` + +Changes to CPU/memory/home-mount only apply after a stop/start cycle: + +```bash +container machine set -n dev cpus=4 +container machine stop dev # must run after set +container machine run -n dev # boot with new config +``` + +### Machine appears but is unreachable + +Rare vmnet subnet conflict on macOS 15. If your machine has no network access: +1. Stop the machine: `container machine stop <name>` +2. Stop the container system: `container system stop` +3. Start again: `container system start` +4. Boot the machine: `container machine run -n <name>` + +## Common Pitfalls + +1. **macOS 15 network isolation**: Container-to-container communication does not work on macOS 15. Only the host can reach the container's IP. +2. **No default kernel**: `container system start` prompts to install one automatically. If skipped, run `container system kernel set --recommended`. +3. **SSH host key changes**: After deleting and recreating a machine with the same DNS name, `ssh` will warn about host key change. Remove the old key from `~/.ssh/known_hosts`. +4. **Home-mount read-only**: `--home-mount ro` causes any write to `$HOME` to fail. Use `rw` unless you have a specific security reason. +5. **Forgetting to stop before delete**: `container machine rm` stops the machine first, but data loss is still permanent. +6. **Images without `/sbin/init`**: A container machine will not boot from a standard application image (e.g., `python:alpine`). Use images built with `/sbin/init`. +7. **Disk space**: Each machine has its own persistent disk. Monitor usage with `container system df`. +8. **Multiple machines sharing the same home directory**: Concurrent writes from multiple machines to the same files can cause conflicts. Use different working directories per machine. +9. **`$HOME` is not `/Users/<user>`**: The machine sets `$HOME` to `/home/<user>` but the mounted macOS home is at `/Users/<user>`. SSH identity file paths in `~/.ssh/config` resolve relative to `$HOME` and will point to `/home/<user>/.ssh/` where keys don't exist. Symlink `/home/<user>/.ssh` to `/Users/<user>/.ssh` to fix. +10. **Shell quoting with `container machine run`**: Avoid `&&`, heredocs, and pipes in commands passed to `container machine run`. The command wrapper re-interprets quoting. Use single commands or invoke scripts from the mounted home directory instead. + +## Sources + +- [Apple container GitHub repo](https://github.com/apple/container) +- `docs/container-machine.md` — full container machine guide +- `docs/command-reference.md` — Machine section with all flags +- `docs/how-to.md` — container machines how-to +- `examples/container-machine-vscode/README.md` — VSCode Remote-SSH walkthrough |
