fix(api): 未配置 SMTP 时发码门统一 503 mail_unavailable 且不再把验证码写日志,非本机中继默认强制 STARTTLS(require_tls)
This commit is contained in:
27 files changed
+529
-87
No files matched your search
@@ -116,12 +116,11 @@ worth revisiting.
|
||||
|
||||
## Wired since the marker was written
|
||||
|
||||
- `internal/api/handlers_email_otp.go:54,223,227` and `internal/api/api.go:84` —
|
||||
SMTP shipped on
|
||||
2026-07-20 (`internal/mail`, wired at `cmd/felis/api.go:264`). The nil-`Mailer`
|
||||
branch that logs the code server-side is a runtime fallback for an install with no
|
||||
`[smtp]` section, not an unbuilt feature. The comments are accurate; the reading
|
||||
"Felis cannot send mail" is not.
|
||||
- `internal/api/handlers_email_otp.go` and `internal/api/api.go` — SMTP shipped on
|
||||
2026-07-20 (`internal/mail`, wired in `cmd/felis/api.go`). An install with no
|
||||
`[smtp]` section leaves the `Mailer` nil, and every door that mails a code answers
|
||||
503 `mail_unavailable`; codes are never logged. The reading "Felis cannot send
|
||||
mail" is stale.
|
||||
- `internal/config/config.go:117` — was stale. It described the upload transport as a
|
||||
deferred integration after both backends had shipped (`LocalContextStore`,
|
||||
`S3ContextStore`, selected in `cmd/felis/api.go` by the shape of the configured
|
||||
|
||||
+24
-2
@@ -192,6 +192,15 @@ components:
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/Error' }
|
||||
MailUnavailable:
|
||||
description: >
|
||||
This install has no [smtp] relay (code mail_unavailable), so no code was minted
|
||||
or sent. The public doors answer it before resolving the address, so it is the
|
||||
same for every address. Sign in with a passkey, or have the operator configure
|
||||
email with felis setup.
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/Error' }
|
||||
RateLimited:
|
||||
description: >
|
||||
This client address called the public sign-in doors faster than the per-address
|
||||
@@ -2162,7 +2171,10 @@ paths:
|
||||
'200':
|
||||
description: >-
|
||||
The login methods available for the address, in a deterministic order
|
||||
(passkey before email_otp). An empty array means no verified account.
|
||||
(passkey before email_otp). email_otp is offered only when the install has
|
||||
a mail relay, passkey only when a verifier is wired and the account has a
|
||||
credential. An empty array means no verified account, or none of its methods
|
||||
is available on this install.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
@@ -2546,6 +2558,8 @@ paths:
|
||||
schema: { $ref: '#/components/schemas/Error' }
|
||||
'502':
|
||||
$ref: '#/components/responses/MailUndeliverable'
|
||||
'503':
|
||||
$ref: '#/components/responses/MailUnavailable'
|
||||
|
||||
/api/v1/auth/email/verify:
|
||||
post:
|
||||
@@ -2673,6 +2687,8 @@ paths:
|
||||
schema: { $ref: '#/components/schemas/Error' }
|
||||
'502':
|
||||
$ref: '#/components/responses/MailUndeliverable'
|
||||
'503':
|
||||
$ref: '#/components/responses/MailUnavailable'
|
||||
|
||||
/api/v1/auth/op-login/status/{id}:
|
||||
get:
|
||||
@@ -4113,7 +4129,7 @@ paths:
|
||||
email: { type: string, format: email }
|
||||
responses:
|
||||
'202':
|
||||
description: Code minted and dispatched (or logged server-side when no mailer is wired).
|
||||
description: Code minted and mailed.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
@@ -4142,6 +4158,8 @@ paths:
|
||||
schema: { $ref: '#/components/schemas/Error' }
|
||||
'502':
|
||||
$ref: '#/components/responses/MailUndeliverable'
|
||||
'503':
|
||||
$ref: '#/components/responses/MailUnavailable'
|
||||
|
||||
/api/v1/account/email/verify:
|
||||
post:
|
||||
@@ -4540,6 +4558,8 @@ paths:
|
||||
schema: { $ref: '#/components/schemas/Error' }
|
||||
'502':
|
||||
$ref: '#/components/responses/MailUndeliverable'
|
||||
'503':
|
||||
$ref: '#/components/responses/MailUnavailable'
|
||||
|
||||
/api/v1/account/reauth/email/verify:
|
||||
post:
|
||||
@@ -4757,6 +4777,8 @@ paths:
|
||||
schema: { $ref: '#/components/schemas/Error' }
|
||||
'502':
|
||||
$ref: '#/components/responses/MailUndeliverable'
|
||||
'503':
|
||||
$ref: '#/components/responses/MailUnavailable'
|
||||
|
||||
/api/v1/account/migrate/confirm/otp/verify:
|
||||
post:
|
||||
|
||||
+30
-2
@@ -1947,11 +1947,12 @@ Skips the pre-migration snapshot (`migrate up -no-backup`). The installer warns
|
||||
loudly when it is set. Use it only when the snapshot cannot work and you have
|
||||
another backup, e.g. an external database newer than the host's `pg_dump`.
|
||||
|
||||
## 17. Sign-in refused with 429, mail budget, account code locks, failed sign-ins
|
||||
## 17. Sign-in refused: 429 limits, no mail relay, account code locks, failed sign-ins
|
||||
|
||||
The public sign-in doors (`/api/v1/auth/*` except logout and the op-login
|
||||
status poll) have three limits of their own. Each answers 429 with a
|
||||
`Retry-After` header and a distinct error code.
|
||||
`Retry-After` header and a distinct error code. The doors that mail a code
|
||||
also answer 503 `mail_unavailable` on an install with no mail relay.
|
||||
|
||||
### `rate_limited`: one address called the doors too often
|
||||
|
||||
@@ -1991,6 +1992,33 @@ raise `max_per_hour` to what your relay allows.
|
||||
(`felis_mail_total{result="failed"}`, 502 `mail_undeliverable` to the caller).
|
||||
The relay's reason is in the `felis-api` log.
|
||||
|
||||
### `mail_unavailable`: no mail relay
|
||||
|
||||
With no `[smtp]` section every door that mails a code answers 503
|
||||
`mail_unavailable` before minting one: email sign-in, op.console sign-in,
|
||||
email verification, and the email step-up for sensitive changes and
|
||||
migration. The public doors answer before looking up the address, so every
|
||||
address gets the same reply. Sign-in is by passkey only, and a verified email
|
||||
stops counting as a way into the account (it is no longer offered as a
|
||||
re-verification factor). Codes are never logged: `felis api` says at start
|
||||
`[smtp] not configured`. Run `felis setup` and configure email to open the
|
||||
doors.
|
||||
|
||||
### Relay refused for lacking TLS
|
||||
|
||||
A relay on port 465 is spoken to over TLS from the first byte. On any other
|
||||
port Felis upgrades with STARTTLS, and when the relay does not offer it the
|
||||
send fails with `smtp: <host>:<port> does not offer STARTTLS` (502
|
||||
`mail_undeliverable` to the caller, the full text in the `felis-api` log, and
|
||||
the same error on the `felis setup` email screen). Without TLS anyone on the
|
||||
path reads the codes, and anyone who can rewrite the conversation can strip
|
||||
the STARTTLS offer, so this is the default for every relay except one on this
|
||||
host (`localhost`, `127.0.0.0/8`, `::1`). Use port 465 or a relay that offers
|
||||
STARTTLS. For a relay you reach over a link you trust, set
|
||||
`require_tls = false` under `[smtp]` in `/etc/felis/felis.toml` and
|
||||
`/etc/felis/felis.pod.toml`; installer re-runs and the setup email screen keep
|
||||
it. `felis api` warns at start whenever codes may go out without TLS.
|
||||
|
||||
### `otp_account_locked`: ten wrong codes in 24 hours
|
||||
|
||||
Ten wrong email codes for one account within 24 hours, counted across every
|
||||
|
||||
Reference in new issue
Block a user