#!/bin/bash # fbenv-spike.sh — R-1126 Part 1a, inside scratch guest 9202. A THROWAWAY FileBrowser (r1126-fb, its own volume) with # FILEBROWSER_ADMIN_PASSWORD from a 0600 env file (generated here, never printed). Prints status codes only. set -u IMG=gtstef/filebrowser:1.5.6-stable; W=/root/r1126; rm -rf $W; mkdir -m 700 -p $W/srv docker rm -f r1126-fb >/dev/null 2>&1; docker volume rm r1126_data >/dev/null 2>&1 cat > $W/config.yaml <<'C' server: port: 80 database: "/home/filebrowser/data/database.db" sources: - path: "/srv/t" name: "T" C umask 077; printf 'FILEBROWSER_ADMIN_PASSWORD=%s\n' "$(head -c 18 /dev/urandom | base64 | tr -dc A-Za-z0-9 | head -c 16)" > $W/env cfgpw() { sed -n 's/^FILEBROWSER_ADMIN_PASSWORD=//p' $W/env; } ip() { docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' r1126-fb; } login() { printf 'X-Password: %s\n' "$1" | curl -s -o /dev/null -w '%{http_code}' --max-time 5 -X POST -H @- "http://$(ip):80/api/auth/login?username=admin"; } token() { printf 'X-Password: %s\n' "$1" | curl -s --max-time 5 -X POST -H @- "http://$(ip):80/api/auth/login?username=admin" | tr -d '"'; } start() { docker run -d --name r1126-fb -v r1126_data:/home/filebrowser/data -v $W/config.yaml:/home/filebrowser/config.yaml:ro \ -v $W/srv:/srv/t -e FILEBROWSER_CONFIG=/home/filebrowser/config.yaml --env-file $W/env $IMG >/dev/null; } echo "== 1. a NEW database with the env password: probe the default login from the first answer (1 s apart, 5 answers —" echo " FileBrowser 1.5.6 limits login tries: the first run of this spike probed 4/s and was answered 429 after 8 tries)" start; seen=""; n=0 for i in $(seq 1 120); do c=$(login admin 2>/dev/null); if [ -n "$c" ] && [ "$c" != "000" ]; then seen="$seen $c"; n=$((n+1)); [ $n -ge 5 ] && break; sleep 1; else sleep 0.25; fi; done echo " default-login answers seen: $(echo $seen | tr ' ' '\n' | sort | uniq -c | tr '\n' ' ')" echo " waiting 90 s for FileBrowser's login limiter to clear"; sleep 90 echo " env password login: $(login "$(cfgpw)")" echo "== 2. the household sets its own password in FileBrowser (the API the box uses), then a restart" tok=$(token "$(cfgpw)"); own="$(head -c 18 /dev/urandom | base64 | tr -dc A-Za-z0-9 | head -c 16)" id=$(curl -s --max-time 5 -H "X-Auth: $tok" -H "Authorization: Bearer $tok" "http://$(ip):80/api/users?id=self" | python3 -c 'import json,sys;d=json.load(sys.stdin);print(d.get("id", ""))') body=$(python3 -c 'import json,sys;print(json.dumps({"which":["password"],"data":{"id":int(sys.argv[1]),"username":"admin","password":sys.stdin.read()}}))' "$id" <<< "$own" | sed 's/\\n"/"/') code=$(printf 'X-Password: %s\n' "$(cfgpw)" | curl -s -o /dev/null -w '%{http_code}' --max-time 5 -X PUT -H @- -H "X-Auth: $tok" -H "Authorization: Bearer $tok" -H 'Content-Type: application/json' --data "$body" "http://$(ip):80/api/users?id=$id") echo " PUT own password: $code; own login: $(login "$own"); env login: $(login "$(cfgpw)")" docker restart r1126-fb >/dev/null; for i in $(seq 1 60); do c=$(curl -s -o /dev/null -w '%{http_code}' --max-time 3 "http://$(ip):80/"); [ "$c" != "000" ] && break; sleep 0.5; done echo " AFTER RESTART — own login: $(login "$own"); env login: $(login "$(cfgpw)"); default login: $(login admin)" docker logs r1126-fb 2>&1 | grep -iE "Resetting admin|admin password from" | sed 's/^/ log: /' | tail -3 docker rm -f r1126-fb >/dev/null; docker volume rm r1126_data >/dev/null; rm -rf $W echo "== cleanup: $(docker ps -a --filter name=r1126 --format '{{.Names}}' | wc -l) containers, $(docker volume ls -q | grep -c r1126) volumes left"