From 2097bec10f05e69cf4d9d26606dbcc9b6a2b4b2d Mon Sep 17 00:00:00 2001 From: Tomas Srnka Date: Thu, 30 Jul 2026 08:07:13 +0000 Subject: [PATCH 1/4] fix(orch): fall back to ID_LIKE with a warning instead of rejecting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switching provisioning to the declared os-release id dropped every Debian derivative that used to work when we probed for a package manager — Kali declares ID=kali ID_LIKE=debian. Unknown ids now retry each ID_LIKE token and warn that the profile is a best-effort guess. rhel/ol/amzn are guarded before the fallback, since both Oracle and Amazon Linux declare ID_LIKE=fedora and would otherwise be silently re-admitted. --- .../build/phases/base/distro/distro.go | 56 +++++++++++++++++-- .../build/phases/base/distro/distro_test.go | 37 ++++++++++++ .../template/build/phases/base/provision.sh | 4 ++ 3 files changed, 91 insertions(+), 6 deletions(-) diff --git a/packages/orchestrator/pkg/template/build/phases/base/distro/distro.go b/packages/orchestrator/pkg/template/build/phases/base/distro/distro.go index cfda4874f8..6e9bd7c107 100644 --- a/packages/orchestrator/pkg/template/build/phases/base/distro/distro.go +++ b/packages/orchestrator/pkg/template/build/phases/base/distro/distro.go @@ -178,13 +178,31 @@ func SupportedIDs() []string { return ids } +// RejectedIDs are distro ids we refuse even though their ID_LIKE names a +// family we do support. Oracle Linux and Amazon Linux both declare +// ID_LIKE=fedora, so without this the ID_LIKE fallback below would quietly +// re-admit exactly the images the rhel profile documents as out of scope. +var RejectedIDs = []string{"rhel", "ol", "amzn"} + // ShellSelector generates the POSIX-sh block provision.sh sources: it switches // on the guest's $E2B_DISTRO_ID and defines the profile's packages, shell -// functions, init path, time-sync unit, admin group and CA handling. An -// unrecognized id exits 1 with a customer-visible error. +// functions, init path, time-sync unit, admin group and CA handling. +// +// An id we don't know falls back to $E2B_ID_LIKE, the derivative-to-parent +// pointer from os-release: Kali declares ID=kali ID_LIKE=debian, and before +// provisioning switched on the declared id, every such Debian derivative worked +// by accident because we probed for a package manager instead. That fallback +// warns rather than fails — the profile is a best-effort guess at that point — +// but an id matching nothing, and an image with no os-release at all, still +// exits 1 rather than being provisioned against a guessed family. func ShellSelector() string { var b strings.Builder - b.WriteString(`case "$E2B_DISTRO_ID" in` + "\n") + + // Selection lives in a function so it can be retried per ID_LIKE token. + // Assignments and function definitions inside a POSIX-sh function are + // global, so the caller sees the profile the same way it always has. + b.WriteString("e2b_select_profile() {\n") + b.WriteString(` case "$1" in` + "\n") for _, p := range Profiles { fmt.Fprintf(&b, " %s)\n", strings.Join(p.IDs, "|")) if p.Bootstrap != "" { @@ -201,14 +219,40 @@ func ShellSelector() string { fmt.Fprintf(&b, " e2b_ca_refresh() { %s; }\n", p.CARefresh) fmt.Fprintf(&b, " E2B_INIT_SYSTEM=%q\n", p.Init) fmt.Fprintf(&b, " e2b_init_setup() {\n%s\n }\n", indentBlock(initSetup[p.Init], " ")) + fmt.Fprintf(&b, " return 0\n") fmt.Fprintf(&b, " ;;\n") } - fmt.Fprintf(&b, " *)\n") + fmt.Fprintf(&b, " *)\n return 1\n ;;\n") + b.WriteString(" esac\n}\n\n") + + b.WriteString(`if ! e2b_select_profile "$E2B_DISTRO_ID"; then` + "\n") + + // Deliberate rejections are checked before the fallback, so they keep + // failing fast with their own reason instead of being matched by ID_LIKE. + fmt.Fprintf(&b, " case \"$E2B_DISTRO_ID\" in\n") + fmt.Fprintf(&b, " %s)\n", strings.Join(RejectedIDs, "|")) + fmt.Fprintf(&b, " echo \"[provision] ERROR: base image distribution ID='$E2B_DISTRO_ID' is not supported.\" >&2\n") + fmt.Fprintf(&b, " echo \"[provision] Sandboxes boot E2B's kernel, so the kABI, signed modules and SELinux these images are chosen for are unavailable.\" >&2\n") + fmt.Fprintf(&b, " exit 1\n") + fmt.Fprintf(&b, " ;;\n") + fmt.Fprintf(&b, " esac\n") + + b.WriteString(" e2b_like_match=\n") + b.WriteString(` for e2b_like in $E2B_ID_LIKE; do` + "\n") + b.WriteString(` if e2b_select_profile "$e2b_like"; then` + "\n") + b.WriteString(" e2b_like_match=$e2b_like\n") + b.WriteString(" break\n") + b.WriteString(" fi\n") + b.WriteString(" done\n") + + b.WriteString(` if [ -z "$e2b_like_match" ]; then` + "\n") fmt.Fprintf(&b, " echo \"[provision] ERROR: unsupported base image distribution: ID='${E2B_DISTRO_ID:-unknown}'.\" >&2\n") fmt.Fprintf(&b, " echo \"[provision] E2B template builds support: %s.\" >&2\n", strings.Join(SupportedIDs(), ", ")) fmt.Fprintf(&b, " exit 1\n") - fmt.Fprintf(&b, " ;;\n") - b.WriteString("esac\n") + b.WriteString(" fi\n") + + fmt.Fprintf(&b, " echo \"[provision] WARNING: base image distribution ID='$E2B_DISTRO_ID' is not officially supported; provisioning it as '$e2b_like_match' from ID_LIKE. This is best effort and untested.\" >&2\n") + b.WriteString("fi\n") return b.String() } diff --git a/packages/orchestrator/pkg/template/build/phases/base/distro/distro_test.go b/packages/orchestrator/pkg/template/build/phases/base/distro/distro_test.go index 7f7096cb71..0612089046 100644 --- a/packages/orchestrator/pkg/template/build/phases/base/distro/distro_test.go +++ b/packages/orchestrator/pkg/template/build/phases/base/distro/distro_test.go @@ -202,3 +202,40 @@ func TestKernelDependentIDsAreRejected(t *testing.T) { } } } + +// An id we don't know falls back to ID_LIKE with a warning instead of failing: +// switching provisioning to the declared id silently dropped every Debian +// derivative (Kali declares ID=kali ID_LIKE=debian) that used to work back when +// we probed for a package manager. +func TestUnknownIDFallsBackToIDLike(t *testing.T) { + t.Parallel() + sel := ShellSelector() + if !strings.Contains(sel, "e2b_select_profile") { + t.Error("selection must be a function so it can be retried per ID_LIKE token") + } + if !strings.Contains(sel, "for e2b_like in $E2B_ID_LIKE; do") { + t.Error("selector must retry each ID_LIKE token") + } + if !strings.Contains(sel, "WARNING") { + t.Error("an ID_LIKE match must warn, not pass silently") + } + // Nothing matched is still fatal — better than provisioning a guessed family. + if !strings.Contains(sel, "unsupported base image distribution") { + t.Error("an id matching neither ID nor ID_LIKE must still fail") + } +} + +// ID_LIKE must not re-admit the ids the rhel profile documents as out of scope: +// Oracle and Amazon Linux both declare ID_LIKE=fedora. +func TestRejectedIDsAreNotReachableViaIDLike(t *testing.T) { + t.Parallel() + sel := ShellSelector() + guard := strings.Join(RejectedIDs, "|") + if !strings.Contains(sel, guard) { + t.Errorf("selector must guard rejected ids (%s) before the ID_LIKE fallback", guard) + } + // The guard has to come first, or ID_LIKE=fedora would match them. + if strings.Index(sel, guard) > strings.Index(sel, "E2B_ID_LIKE") { + t.Error("the rejected-id guard must precede the ID_LIKE fallback") + } +} diff --git a/packages/orchestrator/pkg/template/build/phases/base/provision.sh b/packages/orchestrator/pkg/template/build/phases/base/provision.sh index 0801b43552..b544983b91 100644 --- a/packages/orchestrator/pkg/template/build/phases/base/provision.sh +++ b/packages/orchestrator/pkg/template/build/phases/base/provision.sh @@ -22,8 +22,12 @@ echo "Detecting base image distribution" if [ -r /etc/os-release ]; then . /etc/os-release E2B_DISTRO_ID="${ID:-unknown}" + # Derivative-to-parent pointer, e.g. Kali declares ID=kali ID_LIKE=debian. + # Only consulted when the declared ID matches no profile. + E2B_ID_LIKE="${ID_LIKE:-}" else E2B_DISTRO_ID="unknown (image has no /etc/os-release)" + E2B_ID_LIKE="" fi {{ .DistroSelector }} From 6da97a8c6b47ef65eb64fe18a5efdb9f85da3ab4 Mon Sep 17 00:00:00 2001 From: Tomas Srnka Date: Thu, 30 Jul 2026 11:27:45 +0200 Subject: [PATCH 2/4] test(integration): expect the rejected-id reason for Oracle Linux MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ID_LIKE fallback gave deliberately-rejected ids their own message — "ID='ol' is not supported" plus the kernel/kABI reason — so this test no longer sees the generic "unsupported base image distribution" line. Assert the rejected-id message and that the kABI reason reaches the customer; oraclelinux:9 declares ID_LIKE=fedora, so the case now also proves the rejection guard runs before the fallback. Co-Authored-By: Claude Fable 5 --- .../internal/tests/api/templates/distro_build_test.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/integration/internal/tests/api/templates/distro_build_test.go b/tests/integration/internal/tests/api/templates/distro_build_test.go index f6077443ce..681d2f505a 100644 --- a/tests/integration/internal/tests/api/templates/distro_build_test.go +++ b/tests/integration/internal/tests/api/templates/distro_build_test.go @@ -77,7 +77,9 @@ func TestTemplateBuildDistroFamilies(t *testing.T) { // An image from a distro E2B doesn't provision must be rejected while // provisioning, and the reason must reach the customer instead of a bare exit -// status. Oracle Linux is deliberately unsupported: sandboxes boot E2B's kernel. +// status. Oracle Linux is deliberately unsupported: sandboxes boot E2B's +// kernel, and it declares ID_LIKE=fedora, so it also proves the rejection is +// checked before the ID_LIKE fallback could re-admit it. func TestTemplateBuildUnsupportedDistro(t *testing.T) { t.Parallel() @@ -89,5 +91,6 @@ func TestTemplateBuildUnsupportedDistro(t *testing.T) { outcome := runTemplateBuild(t, "test-distro-unsupported", buildConfig, defaultBuildLogHandler(t)) require.False(t, outcome.ready, "Build of an unsupported distro must fail") - assert.Contains(t, outcome.reason, "unsupported base image distribution") + assert.Contains(t, outcome.reason, "ID='ol' is not supported") + assert.Contains(t, outcome.reason, "Sandboxes boot E2B's kernel") } From a6ae351d22ad50c0c305391f0fcd592e7854b416 Mon Sep 17 00:00:00 2001 From: Tomas Srnka Date: Thu, 30 Jul 2026 11:33:23 +0200 Subject: [PATCH 3/4] fix(orch): report profile match via a flag so errexit survives selection Calling e2b_select_profile as an if-condition ran its whole body with set -e suppressed, so a failing Bootstrap command in a matched arm was ignored until a later command hit the unpopulated PATH. Callers now branch on e2b_profile_matched and the function is invoked as a plain statement. Flagged by Bugbot and Claude review on #3459. Co-Authored-By: Claude Fable 5 --- .../template/build/phases/base/distro/distro.go | 14 ++++++++++---- .../build/phases/base/distro/distro_test.go | 7 +++++++ .../tests/api/templates/distro_build_test.go | 5 ++--- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/packages/orchestrator/pkg/template/build/phases/base/distro/distro.go b/packages/orchestrator/pkg/template/build/phases/base/distro/distro.go index 6e9bd7c107..41dc4edb23 100644 --- a/packages/orchestrator/pkg/template/build/phases/base/distro/distro.go +++ b/packages/orchestrator/pkg/template/build/phases/base/distro/distro.go @@ -201,7 +201,11 @@ func ShellSelector() string { // Selection lives in a function so it can be retried per ID_LIKE token. // Assignments and function definitions inside a POSIX-sh function are // global, so the caller sees the profile the same way it always has. + // The match is reported via e2b_profile_matched, not the return status — + // a function called as an if-condition runs with errexit suppressed, + // which would swallow Bootstrap failures inside a matched arm. b.WriteString("e2b_select_profile() {\n") + b.WriteString(" e2b_profile_matched=\n") b.WriteString(` case "$1" in` + "\n") for _, p := range Profiles { fmt.Fprintf(&b, " %s)\n", strings.Join(p.IDs, "|")) @@ -219,13 +223,14 @@ func ShellSelector() string { fmt.Fprintf(&b, " e2b_ca_refresh() { %s; }\n", p.CARefresh) fmt.Fprintf(&b, " E2B_INIT_SYSTEM=%q\n", p.Init) fmt.Fprintf(&b, " e2b_init_setup() {\n%s\n }\n", indentBlock(initSetup[p.Init], " ")) - fmt.Fprintf(&b, " return 0\n") + fmt.Fprintf(&b, " e2b_profile_matched=1\n") fmt.Fprintf(&b, " ;;\n") } - fmt.Fprintf(&b, " *)\n return 1\n ;;\n") + fmt.Fprintf(&b, " *)\n ;;\n") b.WriteString(" esac\n}\n\n") - b.WriteString(`if ! e2b_select_profile "$E2B_DISTRO_ID"; then` + "\n") + b.WriteString(`e2b_select_profile "$E2B_DISTRO_ID"` + "\n") + b.WriteString(`if [ -z "$e2b_profile_matched" ]; then` + "\n") // Deliberate rejections are checked before the fallback, so they keep // failing fast with their own reason instead of being matched by ID_LIKE. @@ -239,7 +244,8 @@ func ShellSelector() string { b.WriteString(" e2b_like_match=\n") b.WriteString(` for e2b_like in $E2B_ID_LIKE; do` + "\n") - b.WriteString(` if e2b_select_profile "$e2b_like"; then` + "\n") + b.WriteString(` e2b_select_profile "$e2b_like"` + "\n") + b.WriteString(` if [ -n "$e2b_profile_matched" ]; then` + "\n") b.WriteString(" e2b_like_match=$e2b_like\n") b.WriteString(" break\n") b.WriteString(" fi\n") diff --git a/packages/orchestrator/pkg/template/build/phases/base/distro/distro_test.go b/packages/orchestrator/pkg/template/build/phases/base/distro/distro_test.go index 0612089046..0804d6b2ad 100644 --- a/packages/orchestrator/pkg/template/build/phases/base/distro/distro_test.go +++ b/packages/orchestrator/pkg/template/build/phases/base/distro/distro_test.go @@ -223,6 +223,13 @@ func TestUnknownIDFallsBackToIDLike(t *testing.T) { if !strings.Contains(sel, "unsupported base image distribution") { t.Error("an id matching neither ID nor ID_LIKE must still fail") } + // An if-condition call runs the function body with errexit suppressed. + if strings.Contains(sel, "if e2b_select_profile") || strings.Contains(sel, "if ! e2b_select_profile") { + t.Error("e2b_select_profile must not be invoked as an if-condition (errexit suppression)") + } + if !strings.Contains(sel, "e2b_profile_matched=1") { + t.Error("a matched profile arm must set e2b_profile_matched") + } } // ID_LIKE must not re-admit the ids the rhel profile documents as out of scope: diff --git a/tests/integration/internal/tests/api/templates/distro_build_test.go b/tests/integration/internal/tests/api/templates/distro_build_test.go index 681d2f505a..158a6bf9d0 100644 --- a/tests/integration/internal/tests/api/templates/distro_build_test.go +++ b/tests/integration/internal/tests/api/templates/distro_build_test.go @@ -77,9 +77,8 @@ func TestTemplateBuildDistroFamilies(t *testing.T) { // An image from a distro E2B doesn't provision must be rejected while // provisioning, and the reason must reach the customer instead of a bare exit -// status. Oracle Linux is deliberately unsupported: sandboxes boot E2B's -// kernel, and it declares ID_LIKE=fedora, so it also proves the rejection is -// checked before the ID_LIKE fallback could re-admit it. +// status. Oracle Linux declares ID_LIKE=fedora, so this also covers the +// rejection guard running before the ID_LIKE fallback. func TestTemplateBuildUnsupportedDistro(t *testing.T) { t.Parallel() From 8e74ec33c27f7bb12e9631f34d9ff03c81ad4f52 Mon Sep 17 00:00:00 2001 From: Tomas Srnka Date: Thu, 30 Jul 2026 13:07:36 +0200 Subject: [PATCH 4/4] docs(orch): distro package + ShellSelector docs describe the current contract The package doc predates the ID_LIKE fallback ("anything else is rejected"), and the ShellSelector doc told the Kali regression story instead of the behavior. Review nits from #3459. Co-Authored-By: Claude Fable 5 --- .../template/build/phases/base/distro/distro.go | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/packages/orchestrator/pkg/template/build/phases/base/distro/distro.go b/packages/orchestrator/pkg/template/build/phases/base/distro/distro.go index 41dc4edb23..45fb9f1190 100644 --- a/packages/orchestrator/pkg/template/build/phases/base/distro/distro.go +++ b/packages/orchestrator/pkg/template/build/phases/base/distro/distro.go @@ -1,8 +1,9 @@ // Package distro makes template-build provisioning distro-aware: it selects a // declared per-family Profile by the base image's /etc/os-release ID rather than // probing for a package manager. Supported: the systemd family (Debian/Ubuntu, -// Fedora/RHEL/CentOS/Rocky/Alma, Arch) and Alpine on OpenRC; anything else is -// rejected with a clear error. +// Fedora/CentOS/Rocky/Alma, Arch) and Alpine on OpenRC. Derivatives are +// provisioned via ID_LIKE as a best-effort guess with a warning; kernel-dependent +// ids (RejectedIDs) and anything unmatched are rejected with a clear error. package distro import ( @@ -188,13 +189,9 @@ var RejectedIDs = []string{"rhel", "ol", "amzn"} // on the guest's $E2B_DISTRO_ID and defines the profile's packages, shell // functions, init path, time-sync unit, admin group and CA handling. // -// An id we don't know falls back to $E2B_ID_LIKE, the derivative-to-parent -// pointer from os-release: Kali declares ID=kali ID_LIKE=debian, and before -// provisioning switched on the declared id, every such Debian derivative worked -// by accident because we probed for a package manager instead. That fallback -// warns rather than fails — the profile is a best-effort guess at that point — -// but an id matching nothing, and an image with no os-release at all, still -// exits 1 rather than being provisioned against a guessed family. +// An unknown id retries each $E2B_ID_LIKE token in order and provisions the +// first matching family — best effort, with a customer-visible warning. +// RejectedIDs, ids matching nothing, and images without /etc/os-release exit 1. func ShellSelector() string { var b strings.Builder