Skip to content

[security] Default --tailscale bootstrap executes Tailscale's remote install script as root without pinning #789

Description

@coygeek

[security] Default --tailscale bootstrap executes Tailscale's remote install script as root without pinning

Summary

Crabbox's managed-Linux --tailscale workflow installs Tailscale by default with:

curl -fsSL https://tailscale.com/install.sh | sh

That command is generated into cloud-init and runs as root on the leased machine before tailscale up consumes the one-off auth key. The same function already contains a SHA-256-verified static-archive install path, but that path only runs when CRABBOX_TAILSCALE_INSTALL_MODE=pinned is set. Current documentation describes the default as the Tailscale package install script and documents the pinned mode as an opt-in alternative, so the issue is not missing documentation; it is that the documented default bootstrap still executes remote installer bytes without a Crabbox-controlled digest or signature check.

This is in scope under Crabbox's maintainer-authored security policy because it is an integrity failure in an artifact Crabbox downloads and installs as part of a documented default workflow. It does not depend on hostile tenants sharing a broker or on a trusted repository selecting local automation.

Affected Components

  • Verification target: crabbox main snapshot recorded for github-issue-5002 at commit ab1c29f3fd40fad7898a6bf109edd0a17678c937.
  • Component: managed-Linux cloud-init bootstrap for Tailscale-enabled leases.
  • Policy boundary: crabbox/SECURITY.md lists integrity failures in downloaded default-workflow artifacts as in scope.
  • Operations guidance: crabbox/docs/security.md says Tailscale does not change the SSH/key/expiry model; crabbox/docs/features/tailscale.md documents --tailscale, brokered one-off auth keys, and the default installer mode.

Relevant source evidence:

  • internal/cli/bootstrap.go:1081-1084 appends the Tailscale bootstrap whenever cfg.Tailscale.Enabled is true.
  • internal/cli/bootstrap.go:1602 places cloudInitTailscaleInstallBootstrap() into the generated cloud-init script before tailscale up.
  • internal/cli/bootstrap.go:1642-1645 returns the unpinned remote script installer unless CRABBOX_TAILSCALE_INSTALL_MODE is exactly pinned.
  • internal/cli/bootstrap.go:1646-1659 implements the opt-in pinned branch by downloading a versioned .tgz and checking it with sha256sum -c -.
  • internal/cli/bootstrap_test.go:412-451 asserts the default Tailscale cloud-init contains https://tailscale.com/install.sh.
  • internal/cli/bootstrap_test.go:453-479 asserts pinned mode uses the archive URL and sha256sum -c - and does not use tailscale.com/install.sh.
  • docs/features/tailscale.md:27-32 documents crabbox warmup --tailscale and crabbox run --tailscale.
  • docs/features/tailscale.md:195-199 documents that the default installer mode runs Tailscale's package install script and that CRABBOX_TAILSCALE_INSTALL_MODE=pinned enables SHA-256 verification.

Attack Path

Attacker role:

An external supply-chain or network-position attacker who can cause the leased machine to receive malicious content for https://tailscale.com/install.sh. Examples include compromise of the upstream install script or its hosting/CDN path, or an enterprise/TLS-intercepting network position in the lease's egress path.

Prerequisites:

  • The operator creates a managed Linux lease with Tailscale enabled, such as crabbox warmup --tailscale or crabbox run --tailscale -- ....
  • CRABBOX_TAILSCALE_INSTALL_MODE is unset or set to the documented default package/script mode.
  • The attacker controls the script body returned to the lease for https://tailscale.com/install.sh.

Steps:

  1. The operator requests a managed Linux lease with --tailscale.
  2. cloudInitOptionalBootstrap includes the Tailscale bootstrap because cfg.Tailscale.Enabled is true.
  3. cloudInitTailscaleBootstrap emits cloudInitTailscaleInstallBootstrap() before the later tailscale up command.
  4. In the default mode, cloudInitTailscaleInstallBootstrap emits retry sh -c 'curl -fsSL https://tailscale.com/install.sh | sh'.
  5. Cloud-init executes the attacker-controlled script as root on the lease.
  6. The malicious script can persist on the lease, tamper with the Tailscale installation, and as root can inspect cloud-init/user-data material that includes the later one-off auth-key bootstrap.

Control flow:

documented --tailscale lease
  -> cfg.Tailscale.Enabled
  -> cloudInitTailscaleBootstrap
  -> default cloudInitTailscaleInstallBootstrap
  -> curl https://tailscale.com/install.sh | sh
  -> root execution inside the managed Linux lease

Impact

Successful exploitation gives the attacker arbitrary root code execution inside managed Linux leases booted with the default --tailscale installer mode. The malicious code runs before Tailscale enrollment completes, so it can tamper with the installed client/daemon and can read bootstrap material available to root on the machine, including the one-off Tailscale auth-key content embedded later in the cloud-init flow.

The realistic blast radius is the affected lease and its bootstrap-time Tailscale identity, not the coordinator as a hostile multi-tenant platform. That still crosses a Crabbox-supported boundary because Crabbox itself is downloading and executing an external installer as part of a documented default workflow without binding the bytes to a reviewed digest or signature.

Severity Assessment

CVSS Assessment

Metric v3.1 v4.0
Score 8.3 / 10.0 7.7 / 10.0
Severity High High
Vector CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:H CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:P/VC:H/VI:H/VA:H/SC:L/SI:L/SA:N
Calculator CVSS v3.1 Calculator CVSS v4.0 Calculator

The attack is network-adjacent to the external installer delivery path and requires the operator to create a Tailscale-enabled lease, so attack complexity is high and user interaction is required. If the installer bytes are compromised at bootstrap time, impact to the lease is high for confidentiality, integrity, and availability. Secondary impact to the Tailscale enrollment material is represented as limited subsequent-system impact in CVSS v4.0 rather than as full compromise of the entire tailnet.

Recommended Remediation

Make the SHA-256-verified static archive install path the default for managed-Linux --tailscale bootstrap, and reserve the current package-script installer for an explicit compatibility opt-out.

Concrete implementation direction:

  • In cloudInitTailscaleInstallBootstrap, run the existing pinned branch by default.
  • Keep defaultTailscaleVersion, defaultTailscaleAMD64SHA256, and defaultTailscaleARM64SHA256 as the reviewed defaults.
  • Keep CRABBOX_TAILSCALE_VERSION, CRABBOX_TAILSCALE_SHA256_AMD64, and CRABBOX_TAILSCALE_SHA256_ARM64 for reviewed updates and custom deployments.
  • Rename or document the unpinned path as an explicit opt-out, for example CRABBOX_TAILSCALE_INSTALL_MODE=package, and make that mode visibly less safe.
  • Update docs/features/tailscale.md and deployment docs so they describe the pinned default and the explicit package-script fallback.
  • Add or update tests so default generated cloud-init contains the versioned archive URL and sha256sum -c -, and so the package-script string appears only when the explicit fallback mode is selected.

Validation

Validation method:

Source review of the isolated verification workspace, with maintainer-authored policy and operations documents checked by SHA-256 against the verifier prompt.

Evidence:

  • crabbox/SECURITY.md SHA-256 matched 43cfd2f02d69e7febedd509695f498ae77fd9be1e2d84c8c2e9e8f504d63a85a.
  • crabbox/docs/security.md SHA-256 matched f51c7a3113abea1cc822c6161893d1fa9b451c65e77cb4ea358250066a20a962.
  • internal/cli/bootstrap.go:1643-1644 shows the default branch returns curl -fsSL https://tailscale.com/install.sh | sh.
  • internal/cli/bootstrap.go:1646-1659 shows the in-tree pinned mode downloads a versioned Tailscale archive and verifies it with sha256sum -c -.
  • internal/cli/bootstrap_test.go:412-451 confirms the current default generated cloud-init is expected by tests to include https://tailscale.com/install.sh.
  • docs/features/tailscale.md:195-199 confirms current documentation calls the package installer the default and pinned mode opt-in.
  • CVSS values were checked with the local cvss Python package: v3.1 score 8.3 High and v4.0 score 7.7 High for the vectors above.

Counterevidence considered:

  • Pinned mode exists and is documented in the current checkout. That reduces the issue to the default path, but it does not remove the default-workflow integrity gap.
  • The attack requires compromise or tampering of an external installer delivery path at lease boot time, so the finding is High rather than Critical.
  • No live lease was launched during verification. The source and existing unit tests are sufficient to prove the generated default bootstrap string and the opt-in pinned alternative.

Suggested regression check:

go test ./internal/cli/ -run 'TestCloudInitTailscale'

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions