1 verifying your setup
Sam Bent edited this page 2026-07-17 15:15:52 -04:00

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