From aa62e0dfd8101f98aa06a68ee8e7ba1e6a01d1d9 Mon Sep 17 00:00:00 2001 From: Anisa Oshafi Date: Mon, 30 Mar 2026 15:30:36 +0200 Subject: [PATCH 1/2] fix: HOME fallback for custom container user --- cmd/localstack/main.go | 2 + cmd/localstack/user.go | 18 ++++++++- test-homedir.md | 85 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 test-homedir.md diff --git a/cmd/localstack/main.go b/cmd/localstack/main.go index 9bd5b938..b03d877b 100644 --- a/cmd/localstack/main.go +++ b/cmd/localstack/main.go @@ -170,6 +170,8 @@ func main() { } } + EnsureHome() + // file watcher for hot-reloading fileWatcherContext, cancelFileWatcher := context.WithCancel(context.Background()) diff --git a/cmd/localstack/user.go b/cmd/localstack/user.go index 3e6da42f..9ce68750 100644 --- a/cmd/localstack/user.go +++ b/cmd/localstack/user.go @@ -3,12 +3,13 @@ package main import ( "fmt" - log "github.com/sirupsen/logrus" "os" "os/user" "strconv" "strings" "syscall" + + log "github.com/sirupsen/logrus" ) // AddUser adds a UNIX user (e.g., sbx_user1051) to the passwd and shadow files if not already present @@ -82,6 +83,21 @@ func UserLogger() *log.Entry { }) } +// EnsureHome sets HOME=/tmp if the current process has no /etc/passwd entry. +// UnsetLsEnvs strips HOME for AWS parity, which is fine in the normal +// root-start flow where AddUser has written a passwd entry. But when the +// container is launched with --user=1000:1000, AddUser is never called and +// Node's os.homedir() / AWS SDK config loading fail with ENOENT. +func EnsureHome() { + if _, err := user.Current(); err != nil { + if setErr := os.Setenv("HOME", "/tmp"); setErr != nil { + log.Warnln("Could not set HOME=/tmp for non-passwd user:", setErr) + } else { + log.Debugln("No /etc/passwd entry for current UID; HOME set to /tmp") + } + } +} + // DropPrivileges switches to another UNIX user by dropping root privileges // Initially based on https://stackoverflow.com/a/75545491/6875981 func DropPrivileges(userToSwitchTo string) error { diff --git a/test-homedir.md b/test-homedir.md new file mode 100644 index 00000000..5c50e036 --- /dev/null +++ b/test-homedir.md @@ -0,0 +1,85 @@ +# Test: Home Directory Fix (Node.js 22, --user=1000:1000) + +## Step 1 — Build the binary + +```bash +make compile-with-docker +# produces bin/aws-lambda-rie-x86_64 +``` + +## Step 2 — Create the function file + +```bash +mkdir -p /tmp/fn-homedir +cat > /tmp/fn-homedir/index.js << 'EOF' +exports.handler = async () => { + const os = require("os"); + const homeEnv = process.env.HOME; + const debug = { + HOME_env: homeEnv === undefined ? null : homeEnv, + has_HOME_key: Object.prototype.hasOwnProperty.call(process.env, "HOME"), + }; + try { + const h = os.homedir(); + return { statusCode: 200, body: JSON.stringify({ ok: true, ...debug, homedir: h }) }; + } catch (e) { + return { + statusCode: 500, + body: JSON.stringify({ + ok: false, ...debug, + name: e.name, code: e.code, message: e.message, + syscall: e.syscall, errno: e.errno, info: e.info, + }), + }; + } +}; +EOF +``` + +## Step 3 — Start LocalStack with your binary and --user=1000:1000 + +The binary path must be mounted into the LocalStack container via `DOCKER_FLAGS`, then +referenced via `LAMBDA_INIT_BIN_PATH` using the in-container path. + +```bash +DOCKER_FLAGS="-v $(pwd)/bin:/lambda-bin" \ +LAMBDA_DOCKER_FLAGS="--user=1000:1000" \ +LAMBDA_INIT_BIN_PATH=/lambda-bin/aws-lambda-rie-x86_64 \ +localstack start -d + +localstack wait -t 60 +``` + +> **Note:** LocalStack warns to use `LOCALSTACK_`-prefixed env vars — both forms work. + +## Step 4 — Deploy and invoke + +```bash +# Zip the function +cd /tmp/fn-homedir && zip function.zip index.js && cd - + +# Create the function +awslocal lambda create-function \ + --function-name homedir-test \ + --runtime nodejs22.x \ + --handler index.handler \ + --role arn:aws:iam::000000000000:role/lambda-role \ + --zip-file fileb:///tmp/fn-homedir/function.zip + +# Wait for it to be active +awslocal lambda wait function-active --function-name homedir-test + +# Invoke and pretty-print the response +awslocal lambda invoke \ + --function-name homedir-test \ + --payload '{}' \ + /tmp/fn-homedir/response.json && cat /tmp/fn-homedir/response.json +``` + +## Expected output (with the EnsureHome() fix) + +```json +{"statusCode":200,"body":"{\"ok\":true,\"HOME_env\":\"/tmp\",\"has_HOME_key\":true,\"homedir\":\"/tmp\"}"} +``` + +Without the fix you'd get `statusCode: 500` with `"code":"ENOENT"`. From e1b5ffe07dc05c308cbfdc06d73c3b9004fa4e5c Mon Sep 17 00:00:00 2001 From: Anisa Oshafi Date: Tue, 31 Mar 2026 16:03:17 +0200 Subject: [PATCH 2/2] Remove temporary markdown --- test-homedir.md | 85 ------------------------------------------------- 1 file changed, 85 deletions(-) delete mode 100644 test-homedir.md diff --git a/test-homedir.md b/test-homedir.md deleted file mode 100644 index 5c50e036..00000000 --- a/test-homedir.md +++ /dev/null @@ -1,85 +0,0 @@ -# Test: Home Directory Fix (Node.js 22, --user=1000:1000) - -## Step 1 — Build the binary - -```bash -make compile-with-docker -# produces bin/aws-lambda-rie-x86_64 -``` - -## Step 2 — Create the function file - -```bash -mkdir -p /tmp/fn-homedir -cat > /tmp/fn-homedir/index.js << 'EOF' -exports.handler = async () => { - const os = require("os"); - const homeEnv = process.env.HOME; - const debug = { - HOME_env: homeEnv === undefined ? null : homeEnv, - has_HOME_key: Object.prototype.hasOwnProperty.call(process.env, "HOME"), - }; - try { - const h = os.homedir(); - return { statusCode: 200, body: JSON.stringify({ ok: true, ...debug, homedir: h }) }; - } catch (e) { - return { - statusCode: 500, - body: JSON.stringify({ - ok: false, ...debug, - name: e.name, code: e.code, message: e.message, - syscall: e.syscall, errno: e.errno, info: e.info, - }), - }; - } -}; -EOF -``` - -## Step 3 — Start LocalStack with your binary and --user=1000:1000 - -The binary path must be mounted into the LocalStack container via `DOCKER_FLAGS`, then -referenced via `LAMBDA_INIT_BIN_PATH` using the in-container path. - -```bash -DOCKER_FLAGS="-v $(pwd)/bin:/lambda-bin" \ -LAMBDA_DOCKER_FLAGS="--user=1000:1000" \ -LAMBDA_INIT_BIN_PATH=/lambda-bin/aws-lambda-rie-x86_64 \ -localstack start -d - -localstack wait -t 60 -``` - -> **Note:** LocalStack warns to use `LOCALSTACK_`-prefixed env vars — both forms work. - -## Step 4 — Deploy and invoke - -```bash -# Zip the function -cd /tmp/fn-homedir && zip function.zip index.js && cd - - -# Create the function -awslocal lambda create-function \ - --function-name homedir-test \ - --runtime nodejs22.x \ - --handler index.handler \ - --role arn:aws:iam::000000000000:role/lambda-role \ - --zip-file fileb:///tmp/fn-homedir/function.zip - -# Wait for it to be active -awslocal lambda wait function-active --function-name homedir-test - -# Invoke and pretty-print the response -awslocal lambda invoke \ - --function-name homedir-test \ - --payload '{}' \ - /tmp/fn-homedir/response.json && cat /tmp/fn-homedir/response.json -``` - -## Expected output (with the EnsureHome() fix) - -```json -{"statusCode":200,"body":"{\"ok\":true,\"HOME_env\":\"/tmp\",\"has_HOME_key\":true,\"homedir\":\"/tmp\"}"} -``` - -Without the fix you'd get `statusCode: 500` with `"code":"ENOENT"`.