Files
Paul Hitt f95cd947e5 Merge feat/fed-reports-metrics
# Conflicts:
#	Sources/FlamenetRelay/Config.swift
#	Sources/FlamenetRelay/Migrations.swift
2026-10-14 08:40:35 -04:00

5.1 KiB

Security

Reporting a vulnerability

Email security@flamenetmessenger.com. Please do not open a public issue for anything affecting confidentiality, authentication, or availability of a running relay. We acknowledge within 72 hours.

If you want to encrypt your report, the address is also reachable through the contact on https://flamenetmessenger.com/.well-known/security.txt, which this relay serves itself.

Status

Not independently audited. The relay has been reviewed in-house and is exercised by a 253-check conformance suite (flamenet-e2e/test/relay.mjs) that runs against a real binary, plus its own unit tests. A passing suite shows the code does what the spec says; it does not show the spec is right.

An internal adversarial audit found and fixed, among others: a sender-attribution flaw where a claimed user id was trusted without binding it to the authenticated device, a device revocation that did not survive its own token, and per-IP limits defeated by a spoofed X-Forwarded-For. Those are fixed; the exercise is a reason to expect more, not fewer, findings from outside review.

What this relay is trusted with, and what it is not

It stores public keys and opaque ciphertext, and it holds no key that opens a message. It is not trusted to be honest about which keys it serves: that is what the append-only key-transparency log (/e2e/log/*) and client-side pinning exist to constrain, and why every log head is signed.

It necessarily sees delivery metadata, who is addressed, when, and roughly how much. /metadata on a running relay states exactly what is kept and for how long, rendered from that instance's own configuration.

Operator responsibilities

  • Put it behind a reverse proxy, pin the proxy's address, and set FN_TRUSTED_PROXY_ADDRS to that address. The relay reads X-Forwarded-For only from those peers, counting FN_TRUSTED_PROXIES hops from the right. Unset, every per-IP limit is keyed on the proxy's address, so sign-in throttling and sign-up limits apply to everyone at once.
  • Keep FN_ISSUER_PRIVATE_KEY and FN_AT_REST_SECRET out of the image and off shared disks. A leaked issuer key lets someone mint tokens for any account.
  • Pin every federation peer's key in FN_FEDERATION_PEERS (host=key). An unpinned peer is believed with whatever key its descriptor serves, and boot logs a warning for each one.
  • Keep FN_METRICS_PORT off the public network. The counters carry no ids, and they still describe your traffic.
  • Raise the file-descriptor limit (ulimits: nofile: 65536). Each held long-poll costs two; the default 1024 caps you near 450 open apps.
  • Back up the database and rehearse the restore. docs/CAPACITY.md has the measured limits; a backup nobody has restored is a hypothesis.

Account-service tokens (FN_ACCOUNT_TOKEN_STRICT)

POST /api/flamenet/v1/messenger/session trades an account-service token for a relay credential. Today the relay checks the signature, sub and exp. The account service stamps no iss, aud or jti, so the relay cannot tell a token minted for it from one minted for the file server, and the same token can be traded twice.

Two settings close that:

  • FN_ACCOUNT_TOKEN_ISSUER: the iss a token must carry.
  • FN_ACCOUNT_TOKEN_STRICT: off by default. Off, a token without iss, aud (equal to FN_RELAY_AUDIENCE, as a string or inside a list) or jti, or with the wrong value, or with a jti already spent, is accepted and logged as account-service token accepted with <faults>; <n> such since start. On, the same token gets a 401. A jti is remembered in the replay cache (Redis when configured) until the token's exp. The relay refuses to boot with strict on and no issuer set.

Order, so nobody is locked out:

  1. Change the account service to stamp iss, aud (this relay's FN_RELAY_AUDIENCE) and a fresh random jti on every token it hands the app for this relay. A token sent here must be minted for this one exchange: the second trade of the same token is refused once strict is on.
  2. Deploy the relay with FN_ACCOUNT_TOKEN_ISSUER set and strict still off.
  3. Watch the log for account-service token accepted with until it stops appearing for live sessions (older app builds holding cached tokens drain within the token lifetime).
  4. Set FN_ACCOUNT_TOKEN_STRICT=true and redeploy. To back out, set it to false.

Sign-in limits and app credentials

  • Failed sign-ins are counted per address and username (5 per 15 minutes) and per address across all usernames (FN_SIGNIN_MAX_PER_IP, default 30 per 15 minutes, 0 turns it off). Behind an unpinned proxy every client shares one address, so the second bucket locks out sign-in for everyone after 30 failures; set FN_TRUSTED_PROXY_ADDRS first.
  • Every bcrypt hash and verify runs on the application thread pool, off the event loops that carry polls and sends.
  • App credentials are 32 random bytes minted by the relay. New ones are stored as sha256:<hex>. An older bcrypt row is verified with bcrypt once and rewritten as SHA-256 on that success. Passwords stay on bcrypt.