Table of Contents
Verifying your i2pforge: don't trust, verify
Every security claim in this project is reproducible. Here is exactly how to re-prove each one yourself.
1. The image is what the source says (supply chain)
# Reproducible build: two builds of the same commit are bit-identical.
scripts/build-reproducible.sh verify # requires docker buildx
# Signatures (release images): two independent signatures over the digest,
# all offline, no transparency log, no external trust root. This checks both
# (ML-DSA-65 post-quantum + SSH-FIDO2 YubiKey):
scripts/verify-image.sh sambent.dev/sam/i2pforge:<tag>
See signatures/README.md for the exact per-signature commands.
2. The container is hardened (CIS)
# Against a RUNNING i2pforge container:
git clone https://github.com/docker/docker-bench-security && cd docker-bench-security
sudo ./docker-bench-security.sh -c container_runtime | grep i2p
# Expect: zero [WARN] lines for the i2pforge container.
i2pforge doctor # dir/key modes intact
i2pforge doctor --container i2p # (host CLI) all hardening flags still on
The bundled tests/adversary.sh asserts the runtime facts directly: PID 1 holds
exactly the 5 intended capabilities (0xcb), the i2pd daemon runs with zero
capabilities, the root filesystem is read-only, no management ports are
reachable, the docker socket is absent, and a hostile symlink / 0-byte key in
the bind mount is refused.
3. The address is genuinely derived from the key (not faked)
bash tests/smoke.sh # boots i2pd against a key, checks the .b32.i2p
bash tests/e2e.sh # Phases 1-5: tunnel built, leaseset published,
# backend reachable, address persisted == banner
I2PFORGE_E2E_FULL_FETCH=1 bash tests/e2e.sh # Phase 6: fetch the eepsite
# over the LIVE I2P network from a second router
The derivation base32(sha256(Destination)) is cross-validated against i2pd's
own computed address (they match byte-for-byte).
4. The encrypt-at-rest crypto is standards-correct
python3 -m venv v && v/bin/pip install pytest hypothesis cryptography
v/bin/python -m pytest tests/test_keycrypt_properties.py -q
This runs the RFC 8439 (ChaCha20-Poly1305) and RFC 7914 (scrypt)
known-answer vectors, the encrypt/decrypt roundtrip, per-byte AEAD-tamper
detection, salt+nonce uniqueness, and DoS-bound rejection, against the same
py3-cryptography pinned in the image.
5. The untrusted-input handling can't be subverted
bash tests/encrypt.sh # 49 cases incl. crash-recovery
v/bin/python -m pytest tests/test_entrypoint_properties.py -q # validators
The full assurance methodology (225k-input fuzzing, mutation testing, and the egress/clearnet-leak audit) backs every check above. No input makes the container accept a bad key, crash, or phone home in clear text.
6. It doesn't leak
# Backend has NO internet egress (internal network):
docker exec <backend> wget -T3 -O- https://1.1.1.1 ; echo "expect: failure"
# i2pforge resolver is Docker-internal (no backend-name DNS to clearnet):
docker exec <i2p> cat /etc/resolv.conf # expect: nameserver 127.0.0.11
# No clearnet plaintext (:80) connections, only HTTPS reseed + I2P peers:
docker exec <i2p> cat /proc/net/tcp | awk '{print $3}' | grep -ci ':0050' # expect 0