Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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 (
Expand Down Expand Up @@ -178,13 +179,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 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
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.
// 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, "|"))
if p.Bootstrap != "" {
Expand All @@ -201,14 +220,42 @@ 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, " e2b_profile_matched=1\n")
fmt.Fprintf(&b, " ;;\n")
}
fmt.Fprintf(&b, " *)\n")
fmt.Fprintf(&b, " *)\n ;;\n")
b.WriteString(" esac\n}\n\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.
fmt.Fprintf(&b, " case \"$E2B_DISTRO_ID\" in\n")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The guard matches only the exact $E2B_DISTRO_ID, but the ID_LIKE loop below never checks its tokens against RejectedIDs, so ID=cloudlinux ID_LIKE="rhel fedora" skips past rhel and provisions as the rhel family with just a warning.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm ok with the pass here

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(` 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")
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")
Comment thread
tomassrnka marked this conversation as resolved.
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()
}
Original file line number Diff line number Diff line change
Expand Up @@ -202,3 +202,47 @@ 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")
}
// 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:
// 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")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +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.
// 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()

Expand All @@ -89,5 +90,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")
}
Loading