read -rsp "Source password [hackme]: " — pressing Enter accepts it
.env.example
ICECAST_SOURCE_PASSWORD=hackme
drb-edge-node/app/config.py carries the same default (icecast_source_password: str = "hackme").
Icecast binds all interfaces, so on a node whose operator pressed Enter through setup.sh anyone on that LAN can push audio into the node's stream — the stream the frontend and mobile clients play as live traffic. For a public-safety product, an unauthenticated party being able to inject audio into what users believe is live radio is the part that matters, more than the eavesdropping.
Carried over from the 2026-08-05/06 security audit, which listed it as an open HIGH and where it has sat since.
Fix direction: generate a random source password during setup.sh instead of offering a default, and drop the :-hackme fallback in compose so a missing value fails loudly rather than silently becoming a known credential. Related but separate: node-26#1, where the dashboard is still on its first-boot default in the field.
Rotating this on node-002 is an operational step, not just a code change.
`hackme` is the default Icecast source password in all three places a node can pick one up:
| File | Line |
|---|---|
| `docker-compose.yml` | `ICECAST_SOURCE_PASSWORD: ${ICECAST_SOURCE_PASSWORD:-hackme}` |
| `setup.sh` | `read -rsp "Source password [hackme]: "` — pressing Enter accepts it |
| `.env.example` | `ICECAST_SOURCE_PASSWORD=hackme` |
`drb-edge-node/app/config.py` carries the same default (`icecast_source_password: str = "hackme"`).
Icecast binds all interfaces, so on a node whose operator pressed Enter through `setup.sh` anyone on that LAN can push audio into the node's stream — the stream the frontend and mobile clients play as live traffic. For a public-safety product, an unauthenticated party being able to inject audio into what users believe is live radio is the part that matters, more than the eavesdropping.
Carried over from the 2026-08-05/06 security audit, which listed it as an open HIGH and where it has sat since.
Fix direction: generate a random source password during `setup.sh` instead of offering a default, and drop the `:-hackme` fallback in compose so a missing value fails loudly rather than silently becoming a known credential. Related but separate: node-26#1, where the dashboard is still on its first-boot default in the field.
Rotating this on node-002 is an operational step, not just a code change.
Every fallback is gone rather than replaced with a better default:
icecast/entrypoint.sh refuses to start if either password is empty and prints how to generate one. This is the single hard gate.
docker-compose.yml uses ${VAR:?message}, so a missing value stops the stack at compose time instead of becoming an empty string.
setup.sh now generates a random password when the operator presses Enter (openssl rand -base64 24, /dev/urandom fallback). That is the real behaviour change — a prompt default nobody types over is not a default, it is the value.
.env.example ships both keys empty with the generation command in a comment; README.md marks them required with no default.
drb-edge-node/app/config.py and op25-container/app/main.py no longer carry hackme either.
Client suite: 185 passed.
Still outstanding, and it is the part that actually protects node-002: the deployed node's .env has whatever it was set up with. Nothing here rotates it. Generate a new one on the node with openssl rand -base64 24, put it in .env, and restart the stack.
Also added .gitattributes pinning *.sh to LF (fb13bb8) — git warned that entrypoint.sh would become CRLF in the Windows working copy, and a CRLF shebang inside a Linux container fails as bad interpreter: /bin/sh^M, which shows up only as a container that will not start.
Fixed in `e6aab75`.
Every fallback is gone rather than replaced with a better default:
- `icecast/entrypoint.sh` refuses to start if either password is empty and prints how to generate one. This is the single hard gate.
- `docker-compose.yml` uses `${VAR:?message}`, so a missing value stops the stack at compose time instead of becoming an empty string.
- `setup.sh` now **generates** a random password when the operator presses Enter (`openssl rand -base64 24`, `/dev/urandom` fallback). That is the real behaviour change — a prompt default nobody types over is not a default, it is the value.
- `.env.example` ships both keys empty with the generation command in a comment; `README.md` marks them required with no default.
- `drb-edge-node/app/config.py` and `op25-container/app/main.py` no longer carry `hackme` either.
Client suite: 185 passed.
**Still outstanding, and it is the part that actually protects node-002:** the deployed node's `.env` has whatever it was set up with. Nothing here rotates it. Generate a new one on the node with `openssl rand -base64 24`, put it in `.env`, and restart the stack.
Also added `.gitattributes` pinning `*.sh` to LF (`fb13bb8`) — `git` warned that `entrypoint.sh` would become CRLF in the Windows working copy, and a CRLF shebang inside a Linux container fails as `bad interpreter: /bin/sh^M`, which shows up only as a container that will not start.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
hackmeis the default Icecast source password in all three places a node can pick one up:docker-compose.ymlICECAST_SOURCE_PASSWORD: ${ICECAST_SOURCE_PASSWORD:-hackme}setup.shread -rsp "Source password [hackme]: "— pressing Enter accepts it.env.exampleICECAST_SOURCE_PASSWORD=hackmedrb-edge-node/app/config.pycarries the same default (icecast_source_password: str = "hackme").Icecast binds all interfaces, so on a node whose operator pressed Enter through
setup.shanyone on that LAN can push audio into the node's stream — the stream the frontend and mobile clients play as live traffic. For a public-safety product, an unauthenticated party being able to inject audio into what users believe is live radio is the part that matters, more than the eavesdropping.Carried over from the 2026-08-05/06 security audit, which listed it as an open HIGH and where it has sat since.
Fix direction: generate a random source password during
setup.shinstead of offering a default, and drop the:-hackmefallback in compose so a missing value fails loudly rather than silently becoming a known credential. Related but separate: node-26#1, where the dashboard is still on its first-boot default in the field.Rotating this on node-002 is an operational step, not just a code change.
Fixed in
e6aab75.Every fallback is gone rather than replaced with a better default:
icecast/entrypoint.shrefuses to start if either password is empty and prints how to generate one. This is the single hard gate.docker-compose.ymluses${VAR:?message}, so a missing value stops the stack at compose time instead of becoming an empty string.setup.shnow generates a random password when the operator presses Enter (openssl rand -base64 24,/dev/urandomfallback). That is the real behaviour change — a prompt default nobody types over is not a default, it is the value..env.exampleships both keys empty with the generation command in a comment;README.mdmarks them required with no default.drb-edge-node/app/config.pyandop25-container/app/main.pyno longer carryhackmeeither.Client suite: 185 passed.
Still outstanding, and it is the part that actually protects node-002: the deployed node's
.envhas whatever it was set up with. Nothing here rotates it. Generate a new one on the node withopenssl rand -base64 24, put it in.env, and restart the stack.Also added
.gitattributespinning*.shto LF (fb13bb8) —gitwarned thatentrypoint.shwould become CRLF in the Windows working copy, and a CRLF shebang inside a Linux container fails asbad interpreter: /bin/sh^M, which shows up only as a container that will not start.