# Conflicts: # Sources/FlamenetRelay/Config.swift # Sources/FlamenetRelay/Migrations.swift
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_ADDRSto that address. The relay readsX-Forwarded-Foronly from those peers, countingFN_TRUSTED_PROXIEShops 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_KEYandFN_AT_REST_SECRETout 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_PORToff 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.mdhas 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: theissa token must carry.FN_ACCOUNT_TOKEN_STRICT: off by default. Off, a token withoutiss,aud(equal toFN_RELAY_AUDIENCE, as a string or inside a list) orjti, or with the wrong value, or with ajtialready spent, is accepted and logged asaccount-service token accepted with <faults>; <n> such since start. On, the same token gets a 401. Ajtiis remembered in the replay cache (Redis when configured) until the token'sexp. The relay refuses to boot with strict on and no issuer set.
Order, so nobody is locked out:
- Change the account service to stamp
iss,aud(this relay'sFN_RELAY_AUDIENCE) and a fresh randomjtion 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. - Deploy the relay with
FN_ACCOUNT_TOKEN_ISSUERset and strict still off. - Watch the log for
account-service token accepted withuntil it stops appearing for live sessions (older app builds holding cached tokens drain within the token lifetime). - Set
FN_ACCOUNT_TOKEN_STRICT=trueand redeploy. To back out, set it tofalse.
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; setFN_TRUSTED_PROXY_ADDRSfirst. - 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.