Tutorial: grade ESPot with UHBS (quick + full)¶
Status: Informative · evaluation proof
Audience: Researchers and defenders who want to reproduce the published ESPot reports
Outputs live in: quick/ and full/ · trust notes: METHODOLOGY.md
This is the exact workflow used to produce the artifacts under
docs/conformance/reports/espot/. Product name = proof label only.
0. Prerequisites¶
- Docker Engine or Docker Desktop
git,curl, Python 3.11+ (for localuhbs validate-scorecard)- ~2 GB disk for Node 10 + UHBS images
git clone https://github.com/mziqudhd92/uhbs-standard.git
cd uhbs-standard
# Base grader (CLI + lab)
docker build -t uhbs:4.0.1 .
# Full grader (+ bandit / semgrep; trivy when download succeeds)
docker build -f Dockerfile.full -t uhbs:4.0.1-full .
Confirm:
1. Clone ESPot¶
mkdir -p .local/labs
git clone https://github.com/mycert/ESPot.git .local/labs/ESPot
cd .local/labs/ESPot
git rev-parse HEAD # record in your run-meta if publishing
2. Revive the honeypot (expect modern Node to fail)¶
ESPot targets Node ~0.10 / Express 3. On Node 20, npm install fails on
sqlite3@2 (primordials is not defined). That friction is part of the story.
Shim used for these reports:
- Disable SQLite logging in
config.js. - Run under Node 10 in Docker.
config.js:
module.exports = {
default_response: 'default',
logging: {
sqlite: { enable: false, dbpath: __dirname + "/logs/attack.db" },
http_push: { enable: false, url: "http://localhost/path/path?query=string" }
},
tz: 'GMT+0'
};
Dockerfile next to app.js:
FROM node:10-buster-slim
WORKDIR /opt/espot
RUN npm install --production express@3.16.5 ejs@2.7.4 request@2.88.2 \
&& npm cache clean --force
COPY . .
EXPOSE 9200
CMD ["node", "app.js"]
Build & smoke-test:
docker build -t espot:lab .
docker network create uhbs-lab 2>/dev/null || true
docker rm -f espot-lab 2>/dev/null || true
docker run -d --name espot-lab --network uhbs-lab -p 9200:9200 espot:lab
docker logs espot-lab
# expect: Express server listening on port 9200
curl -sS http://127.0.0.1:9200/
# expect JSON with "tagline": "You Know, for Search"
Return to the UHBS repo root for the next steps:
3. Quick run (smoke grade)¶
Intent: prove the pipeline works fast. Not claim-grade.
mkdir -p docs/conformance/reports/espot/quick
docker run --rm \
--network uhbs-lab \
-v "$PWD:/work" \
-v "$PWD/.local/labs/ESPot:/honeypot:ro" \
-w /work \
-e UHBS_QUICK=1 \
-e UHBS_AIRGAP_ATTESTED=1 \
uhbs:4.0.1 \
lab \
--tps web_api \
--protocol http \
--class Web-API \
--target espot-lab \
--port 9200 \
--source-root /honeypot \
--phases profile,static,sandbox,dynamic,score \
--modules A,B,C,D,E,F \
--quick \
--skip-sast-tools \
--out /work/docs/conformance/reports/espot/quick \
--environment "Quick Docker lab demo (UHBS_QUICK=1, SAST skipped)"
Published result for this tree: UHQS 49.34 / Grade F — see quick/SCORECARD.txt.
Why it still fails despite “HTTP 200 works”:
- Module A is improved on the 0–100 CheckResult scale (86.75) but still flags illegal
HTTP/9.9→ 200. - Module D cannot clear the Safety Gate without stronger containment evidence.
- δ_C = 0.5625 multiplies the weighted sum.
4. Seed telemetry for the full run¶
mkdir -p .local/labs/ESPot-telemetry
for i in $(seq 1 30); do
curl -sS -o /dev/null "http://127.0.0.1:9200/"
curl -sS -o /dev/null "http://127.0.0.1:9200/_search?q=uhbs$i"
curl -sS -o /dev/null -X PUT "http://127.0.0.1:9200/idx/doc/$i" \
-H 'Content-Type: application/json' -d "{\"probe\":$i}"
done
docker cp espot-lab:/opt/espot/logs/. .local/labs/ESPot-telemetry/
printf '%s\n' '# UHBS egress gateway canary — no HIT lines means clean' \
> .local/labs/ESPot-telemetry/egress-gateway.log
Lab assets (already in-repo):
../../labs/espot/web_api_full.yaml— 1000-sample TPS../../labs/espot/inventory.yaml— HTTP-only inventory
If your clone path differs from .local/labs/ESPot, edit source_root / mounts accordingly (inventory paths inside the container stay /honeypot and /telemetry).
5. Full run (claim-grade Docker lab)¶
Intent: deepest practical UHBS-Lab grade in this topology — RFC depth, source, SAST, telemetry, load.
mkdir -p docs/conformance/reports/espot/full
docker run --rm \
--network uhbs-lab \
-v "$PWD:/work" \
-v "$PWD/.local/labs/ESPot:/honeypot:ro" \
-v "$PWD/.local/labs/ESPot-telemetry:/telemetry:ro" \
-w /work \
-e UHBS_AIRGAP_ATTESTED=1 \
-e UHBS_EGRESS_GATEWAY_LOG=/telemetry/egress-gateway.log \
uhbs:4.0.1-full \
lab \
--inventory /work/docs/conformance/labs/espot/inventory.yaml \
--target espot \
--phases profile,static,sandbox,dynamic,score \
--modules A,B,C,D,E,F \
--concurrency 25 \
--requests 200 \
--out /work/docs/conformance/reports/espot/full \
--environment "Detailed Docker lab: RFC9110 + 1000-sample A3 + SAST + telemetry"
Published result: UHQS 63.33 / Grade D — see full/SCORECARD.txt.
After the run, add/update provenance:
# edit run-meta.json with your digests/commits, then refresh MANIFEST hashes
python - <<'PY'
import hashlib, json
from pathlib import Path
root = Path("docs/conformance/reports/espot/full")
# ensure run-meta.json exists, then:
arts = []
for name in ["REPORT.txt","SCORECARD.txt","report.json","uhbs-run.log","run-meta.json"]:
p = root/name
if p.is_file():
arts.append({"path": name, "sha256": hashlib.sha256(p.read_bytes()).hexdigest()})
for p in sorted((root/"static").glob("*.json")):
arts.append({"path": f"static/{p.name}", "sha256": hashlib.sha256(p.read_bytes()).hexdigest()})
rep = json.loads((root/"report.json").read_text())
man = {"uhbs_version":"4.0.1","artifacts":arts,"extra":{
"target": rep["target"]["name"], "uhqs": rep["uhqs"]["uhqs"], "grade": rep["uhqs"]["grade"]}}
(root/"MANIFEST.json").write_text(json.dumps(man, indent=2)+"\n")
print("wrote", root/"MANIFEST.json")
PY
6. Read and verify the artifacts¶
# Human cards
cat docs/conformance/reports/espot/quick/SCORECARD.txt
cat docs/conformance/reports/espot/full/SCORECARD.txt
# Machine evidence (per-check)
python -m json.tool docs/conformance/reports/espot/full/report.json | less
# SAST
ls docs/conformance/reports/espot/full/static/
# Fixture integrity (sanitized full scorecard)
uhbs validate-scorecard docs/conformance/fixtures/espot-web-api.scorecard.json --strict
Flag cheat-sheet¶
| Flag / mount | Why it matters |
|---|---|
--network uhbs-lab |
Grader resolves espot-lab / inventory host |
--source-root / /honeypot |
Module F white-box |
/telemetry + inventory telemetry_dir |
Honest Module C |
UHBS_QUICK=1 / --quick |
Shorter A3 + lighter E |
--skip-sast-tools |
Skip bandit/semgrep/trivy |
uhbs:4.0.1-full |
Installs SAST tools |
UHBS_AIRGAP_ATTESTED=1 |
Phase-3 / D attestation (documented limitation) |
UHBS_EGRESS_GATEWAY_LOG |
Canary file for D1 gateway evidence |
--concurrency / --requests |
Module E depth |
7. Interpret the grades¶
| Observation | Meaning |
|---|---|
| HTTP root returns 200 + ES banner | Liveness ≠ UHBS quality |
| Module A = 86.75 | Decoy still accepts illegal HTTP versions (partial credit on 0–100 scale) |
| Full C = 55 | Real logs lack STIX/OTel/ECS schema |
| D < 95 | Safety Gate applies exponential δ_C |
| Full UHQS 63.33 / D | Below beta Production Baseline (UHQS > 80 + gate) |
| Quick UHQS 49.34 / F | Lower once δ_C is harsh and D is weak |
For trust-facing narrative, cite full/ and METHODOLOGY.md.
8. Cleanup¶
9. Publishing checklist (for new honeypots)¶
When you add another decoy under docs/conformance/reports/<id>/:
- [ ]
quick/andfull/both populated - [ ]
run-meta.jsonwith image digests + commits - [ ]
MANIFEST.jsonhashes refreshed - [ ]
TUTORIAL.md+METHODOLOGY.mdwritten - [ ] Row added to
../index.md - [ ] Optional sanitized fixture under
docs/conformance/fixtures/ - [ ] Explicit non-endorsement language
Back to ESPot report hub · all reports.