Skip to main content
November 9, 2026

Sandboxing curl | sh Installers Before They Touch Your System

Sandboxing curl | sh Installers with installctl

Many tools an SRE team uses ship with the same install line: curl -fsSL https://example.com/install.sh | sh. It is fast, but it hands a remote script your shell, your home directory and often sudo, and leaves no record of what it changed. Six months later, removing the tool is guesswork.

installctl is a new Linux tool that puts a review step in the middle. It runs the installer in a sandbox, shows every file it created, modified or deleted, lets you apply all, some or none of it, and writes an uninstall script. This post covers how it works, where it is blind, and how it handles npm.

The Threat Model of curl | sh

Piping a remote script into a shell has several distinct risks. They need different defenses, so it helps to list them before picking a tool.

Risk What happens Does installctl help?
Silent system changes Files in /usr/local/bin, /opt, lines appended to ~/.bashrc, new services Yes, this is its main job
No uninstaller Removal is guesswork Yes, it writes uninstall-<name>.sh
Root actions sudo apt-get install, useradd, systemctl enable Yes, for the commands it intercepts
Truncated download The connection drops and sh runs half a script Partly: you see the file changes of the half run before they land
Different payload when piped The server detects the pipe and sends other content Only for file changes: you review what the hidden payload wrote
Secret theft The script reads ~/.ssh, ~/.aws or tokens and sends them out No
Malicious binary The installed program does damage on first launch No, review covers install time only

The truncation risk is why careful installers wrap their logic in a function. The rustup installer defines main() near the top and calls main "$@" only on its last lines, so a partial download defines functions but runs nothing.

A 2016 write-up on detecting curl | bash server side shows that a server can start the script with a sleep, fill the TCP buffers, and watch for the pause that happens only when bash executes the stream. It then serves a harmless script to curl alone and a different one to curl | bash.

What installctl Is

installctl is written in Rust and licensed under Apache-2.0. The first release, v0.1.0, was published on 2026-11-08 with a .deb for Ubuntu 24.04 and an .rpm for EL9. It is very new, so treat it as a tool to evaluate, not one to trust blindly.

The README is direct about scope: "It is not a security sandbox. The installer can read your files and use the network." It is about transparency and control over well-behaved installers, not protection from malware.

How It Works

The sandbox code creates three Linux namespaces: user, mount and PID. There is no network namespace, so the installer has your normal network access.

  • Filesystem. The installer's / is a FUSE copy-on-write overlay. Reads come from your real system. Writes go to a staging directory under ~/.local/state/installctl.
  • Fake root. sudo inside the sandbox is emulated, so the installer can "write" to /usr without a password. Those writes are staged like any other.
  • Command shims. Calls to package managers (apt, dnf, pacman, snap, flatpak and others), account tools (useradd, groupadd), service tools (systemctl, service), scheduling (crontab, at) and system tools (ldconfig, modprobe, sysctl) are replaced with shims. installctl pauses and asks you to skip, run for real, skip it and every later call of it, or abort.
  • Review. When the installer exits, you get a list of created, modified and deleted files with sizes, owners and line changes. Background processes the installer left running are listed and stopped.
  • Apply. Accepted changes are written atomically. If some need root, installctl calls the real sudo once, for those paths only.

The generated uninstall script reverts only the installer's own edits to files like ~/.bashrc, so lines you added later stay. It warns before deleting a file that changed since the install and removes directories only when they are empty.

Installation

On Ubuntu or Debian, download the package from the releases page and install it:

sudo apt install ./installctl_*_amd64.deb
grep "^$USER:" /etc/subuid /etc/subgid   # you need subordinate IDs

The package pulls in libfuse3-3 and uidmap. Ubuntu introduced AppArmor restrictions on unprivileged user namespaces in 23.10, so on 24.04 and later the .deb also installs an AppArmor profile for installctl. On EL9, install the .rpm with dnf.

A Worked Example

installctl --name mytool "curl -fsSL https://example.com/install.sh | sh"

A typical session from the README: the script asks for sudo apt-get install -y libfuse2, you press s to skip it (the installer is told it succeeded), and the review shows two created files, ~/.local/bin/mytool and /usr/local/bin/mytool-helper, plus one modified file, ~/.bashrc (+1 line). Press d to see the diff of ~/.bashrc before you decide. Then a applies everything, or s lets you enter numbers to exclude, such as 2 to drop the root-owned helper.

That diff step is where most of the value is. A line appended to a shell profile runs in every future shell, and it is easy to miss because the installer output rarely mentions it.

What the Sandbox Cannot See

The overlay only covers processes inside the sandbox's mount namespace. Anything else writes to the real disk. From the README, the mount setup in init.rs and the namespace design, these are the blind spots:

  • Separate daemons. If the installer asks a process that runs outside the sandbox to do work, for example a service it reaches over a socket or the network, that process writes to your real filesystem. installctl does not see those files. The network is shared, so this path is open.
  • Unknown commands. Only the listed commands are intercepted. A tool that changes the system through another program or direct syscalls is not caught, although its file writes inside the sandbox still are.
  • Temporary directories. /tmp and /var/tmp are fresh tmpfs mounts inside the sandbox. Anything the installer leaves there disappears and never shows in the review.
  • /dev and /sys. These are bind mounts of the host's own directories, not part of the overlay.
  • Reads. Every file you can read, the installer can read and send out.
  • Later behaviour. The review covers what the script did at install time. The binary you approve can download more code the first time you run it.
  • Hard links are applied as separate copies.

A related trap: an installer that starts its own background process without systemctl gets that process stopped at the end of the sandbox run. After you apply the changes, start the service yourself.

npm and Other Package Managers

npm is not in the intercept list, so installctl "npm install -g some-cli" runs npm inside the overlay like any other program. Per the npm folders docs, a global install writes to {prefix}/lib/node_modules and links executables into {prefix}/bin, and the cache lives in ~/.npm. Expect the review to list the whole package tree and the new cache entries, which can be a long list.

The useful part is lifecycle scripts. preinstall and postinstall hooks run inside the sandbox, so a hook that edits ~/.bashrc or drops a file in ~/.config shows up as a change you can reject. What the sandbox does not stop is the pattern from the Keyv npm compromise: a hook that reads tokens and sends them over the network. For that, you need an install with no secrets in reach and no network beyond the registry.

System package managers like apt-get and dnf are intercepted: skip them, or run them for real, outside the review.

When to Use What

Situation Better choice
You trust the vendor and want a record and an uninstaller installctl
You want to see where an unfamiliar script puts files installctl, with the diff view
You do not trust the script A throwaway VM or container with no secrets mounted
You need to block network access or file reads A real sandbox, such as bubblewrap with --unshare-net and only the paths you bind in
Production servers A distro package, a pinned release binary with a checksum, or a container image

A quick checklist before any curl | sh:

  1. Download the script to a file and read it, including what it downloads next.
  2. Check that the logic runs from a function called on the last line.
  3. Run it as a user without cloud credentials or SSH keys loaded.
  4. Use installctl or a disposable environment to see what it changes.
  5. Keep the uninstall script next to your runbook for that host.

Agent-Installed Tooling

An AI agent that runs curl | sh to get a CLI inherits all the risks above, with nobody watching the review menu. The safest setup gives the agent no secrets to leak in the first place. In Akmatori, tools run through the MCP Gateway, which holds the credentials and the authorization, so the agent never sees a password, token or key.

Conclusion

installctl turns curl | sh into a reviewed change with an uninstaller. It shows what an honest installer does to your filesystem, but it does not contain a hostile one: reads, the network and outside daemons stay open. Use it for transparency, and a disposable environment for trust.

If your team wants AI-assisted incident workflows on your own infrastructure, Akmatori helps SRE teams investigate alerts, coordinate response, and automate safe infrastructure actions. Powered by Gcore for global infrastructure reliability.

Automate incident response and prevent on-call burnout with AI-driven agents!