Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
15 changes: 15 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ jobs:
SALT=$(openssl rand -hex 16)
HASH=$(printf 'zebra' | openssl dgst -sha256 -mac HMAC -macopt "key:$SALT" | awk '{print $NF}')
sed -i "s|__GENERATED_BY_INIT_SH__|${SALT}\$${HASH}|" config/regtest/zallet.toml
# Mirror the permission end-state of setup-network.sh / regtest-init.sh
# so the readability assertion below exercises the real contract:
# non-secret TOMLs world-readable, the age key 0600 + ACL for uid 1000.
chmod 644 config/regtest/zallet.toml config/regtest/zaino.toml config/regtest/zebra.toml
chmod 600 config/regtest/zallet_identity.txt
setfacl -m u:1000:r config/regtest/zallet_identity.txt
Comment thread
gustavovalverde marked this conversation as resolved.
Outdated

- name: Start Zebra (regtest)
run: |
Expand Down Expand Up @@ -136,6 +142,15 @@ jobs:
- name: Verify Zallet accepts compose command
run: docker compose --env-file .env.regtest run --rm --no-deps zallet --help

- name: Assert Zallet config is readable by the container uid (1000)
# Zallet runs as uid 1000 (distroless image, no runtime chown). Guards
# the config-readability contract: zallet.toml is world-readable and the
# age key is reachable by uid 1000 via ACL. Catches a regression of the
# mktemp-0600 mode leaking back onto the rewritten config.
run: |
docker run --rm --user 1000:1000 -v "$PWD/config/regtest":/c:ro alpine \
sh -c 'cat /c/zallet.toml >/dev/null && cat /c/zallet_identity.txt >/dev/null && echo "uid 1000 read OK"'

- name: Verify the cookie volume is attachable
# Regtest disables cookie auth (ZEBRA_RPC__ENABLE_COOKIE_AUTH=false) so
# Zebra does not write /auth/.cookie. This check only verifies that the
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,8 @@ Fix permissions before starting:

Zebra, Zaino, and Zallet each run as a specific non-root user. Directories must have correct ownership (set by the script) and `700` permissions. Never use `755` or `777`.

Operators whose host uid is not `1000` do **not** need to coordinate uids for Zallet's bind-mounted config. Zallet runs as uid 1000 (distroless image), and `setup-network.sh` makes `zallet.toml` readable by that uid (`0644`) and grants the age key `zallet_identity.txt` read access for uid 1000 via a POSIX ACL (`setfacl -m u:1000:r`) while keeping it `0600` for everyone else. Install the `acl` package on Linux for this; if `setfacl` is unavailable the script warns and you can fall back to `chmod 644` on the identity file.

</details>

<details>
Expand Down
6 changes: 6 additions & 0 deletions docs/docker-architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,12 @@ The base compose includes a small `cookie-permissions` sidecar (`alpine:3` with

Zaino and Zallet depend on the sidecar's healthcheck, so targeted starts such as `docker compose up -d zaino` also start the sidecar and wait until the cookie is readable. On mainnet and testnet the healthcheck waits for `/var/run/auth/.cookie`; on regtest it exits successfully because cookie auth is disabled and username/password auth is used instead.

## Zallet config readability and operator uid

Zallet uses a distroless image with no shell, entrypoint script, or Linux capabilities, so the base compose pins it with `user: "1000:1000"` and it runs as that uid from PID 1 — unlike Zebra (root entrypoint that drops to uid 10001) and Zaino (root entrypoint with `cap_add: [DAC_OVERRIDE, ...]`, which bypasses file-permission checks before dropping privileges). Zallet therefore can only read its two bind-mounted host config files — `config/<network>/zallet.toml` → `/etc/zallet/zallet.toml` and `config/<network>/zallet_identity.txt` → `/etc/zallet/identity.txt` — if they are readable by uid 1000.

The same "no UID coordination required" property that the cookie sidecar provides applies here: the setup scripts make this config readable by uid 1000 regardless of the operator's host uid. `scripts/setup-network.sh` writes `zallet.toml` (and the other non-secret TOMLs) mode `0644`; `scripts/regtest-init.sh` restores `0644` after its `mktemp`+`mv` pwhash rewrite (which would otherwise leave the file `0600`). The age key `zallet_identity.txt` is a long-lived wallet secret, so instead of widening it to all local users it stays mode `0600` with a POSIX ACL granting read to uid 1000 only (`setfacl -m u:1000:r`). `setfacl` (the `acl` package on Linux, with an ACL-capable filesystem such as ext4/xfs) is a soft prerequisite; if it is unavailable the script warns and the operator can fall back to `chmod 644` on the identity file.

## Regtest overlay constraints

### Zaino authentication
Expand Down
3 changes: 3 additions & 0 deletions scripts/regtest-init.sh
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ update_zallet_rpc_pwhash() {

sed -E "s|^pwhash = \".*\"$|pwhash = \"${pwhash}\"|" "$config_path" > "$tmp"
mv "$tmp" "$config_path"
# mktemp creates the temp file mode 0600 and mv carries that mode onto the
# config; restore world-read so the zallet container (uid 1000) can read it.
chmod 644 "$config_path"
Comment thread
gustavovalverde marked this conversation as resolved.
Outdated

log "==> Generated zallet RPC pwhash in ${CONFIG_DIR}/zallet.toml"
}
Expand Down
13 changes: 13 additions & 0 deletions scripts/setup-network.sh
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ copy_template() {
fi

cp "$example" "$active"
# Make the config world-readable so the pinned zallet container uid (1000)
# can read it regardless of the operator's host uid/umask. These TOMLs are
# not secret; the age key is handled separately in ensure_identity.
chmod 644 "$active"
log "==> $NETWORK/$file: created from .example template."
Comment thread
gustavovalverde marked this conversation as resolved.
}

Expand All @@ -67,6 +71,15 @@ ensure_identity() {

rage-keygen -o "$identity"
chmod 600 "$identity"
# Zallet runs as uid 1000 (distroless image, no runtime chown). Grant that
# uid read access to the age key without widening it to other host users.
if command -v setfacl >/dev/null 2>&1; then
setfacl -m u:1000:r "$identity" \
|| log "WARN: setfacl failed on $identity; zallet (uid 1000) may not be able to read it."
else
log "WARN: setfacl not found. Grant uid 1000 read on $identity before starting zallet"
log " (install the 'acl' package, or 'chmod 644 $identity' to allow all local users)."
fi
Comment thread
gustavovalverde marked this conversation as resolved.
Outdated
log "==> $NETWORK/zallet_identity.txt: generated."
}

Expand Down
Loading