feat(api): 访问日志与 HTTP/运行时指标、安全响应头与面板 CSP、跨站写拦截、请求体读截止、SSE 定期重鉴权与续传、404/405/413 信封、status 按所有权裁剪、停机并发排空
This commit is contained in:
24 files changed
+1453
-55
No files matched your search
+44
-4
@@ -31,6 +31,28 @@ info:
|
||||
and the panel). Admin-tier external operations additionally require the admin
|
||||
Access path. See `x-felis-face` / `x-felis-tier` on each operation.
|
||||
|
||||
Behaviour every operation shares, and so not repeated under each:
|
||||
|
||||
* Every response carries `X-Request-Id` (a well-formed inbound one is kept),
|
||||
`X-Content-Type-Options: nosniff`, `X-Frame-Options: DENY`,
|
||||
`Referrer-Policy: no-referrer` and `Content-Security-Policy: default-src 'none'`.
|
||||
`Strict-Transport-Security` is added when the request came through the TLS
|
||||
edge (`X-Forwarded-Proto: https`).
|
||||
* A path no operation serves is `404 not_found`; a path served under other
|
||||
methods is `405 method_not_allowed` with an `Allow` header.
|
||||
* A POST/PUT/PATCH/DELETE a browser sends from another site (`Sec-Fetch-Site`
|
||||
`same-site` or `cross-site`, or an `Origin` whose host is not the request's)
|
||||
is `403 cross_site`, before authentication. Callers that send neither header
|
||||
(the plugins, scripts) are unaffected.
|
||||
* A JSON body over 1 MiB is `413 too_large`. A request body must keep arriving:
|
||||
after 30 s it has to average 16 KiB/s or the connection is closed.
|
||||
* Event streams (the console and build logs) tag each line with `id:` (unix
|
||||
seconds); an EventSource that reconnects with `Last-Event-ID` within the hour
|
||||
resumes from that second instead of the tailed backlog. The server re-checks
|
||||
the caller every minute and ends the stream with `event: revoked` once the
|
||||
session or the access is gone; a stream also closes after 30 minutes and on
|
||||
server shutdown, and the client simply reconnects.
|
||||
|
||||
servers:
|
||||
- url: https://api-internal.{root_domain}
|
||||
description: >-
|
||||
@@ -1443,9 +1465,16 @@ paths:
|
||||
security: [{ accessJWT: [] }]
|
||||
parameters:
|
||||
- { name: name, in: path, required: true, schema: { type: string } }
|
||||
- name: Last-Event-ID
|
||||
in: header
|
||||
required: false
|
||||
description: The id of the last line received; resumes the stream from that second (within the hour).
|
||||
schema: { type: string }
|
||||
responses:
|
||||
'200':
|
||||
description: An event stream of log lines.
|
||||
description: >-
|
||||
An event stream of log lines (`id:` + `data:` per line, `:` comments as
|
||||
keep-alives), ended by `event: revoked` when access is withdrawn.
|
||||
content:
|
||||
text/event-stream:
|
||||
schema: { type: string }
|
||||
@@ -1871,7 +1900,11 @@ paths:
|
||||
get:
|
||||
tags: [servers]
|
||||
operationId: status
|
||||
summary: Status of your own server.
|
||||
summary: Status of a server; the full record for its owner and staff.
|
||||
description: >-
|
||||
Anyone signed in may ask. The owner and staff get the whole projection;
|
||||
anyone else gets what the game's own server list shows: name, subdomain,
|
||||
displayName, phase, ready, playersOnline and playersMax.
|
||||
x-felis-face: [external]
|
||||
x-felis-tier: app
|
||||
security: [{ accessJWT: [] }]
|
||||
@@ -1879,7 +1912,7 @@ paths:
|
||||
- { name: name, in: path, required: true, schema: { type: string } }
|
||||
responses:
|
||||
'200':
|
||||
description: The server's status projection.
|
||||
description: The server's status projection (trimmed for non-owners).
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/ServerInfo' }
|
||||
@@ -4707,9 +4740,16 @@ paths:
|
||||
security: [{ accessJWT: [] }]
|
||||
parameters:
|
||||
- { name: id, in: path, required: true, schema: { type: string } }
|
||||
- name: Last-Event-ID
|
||||
in: header
|
||||
required: false
|
||||
description: The id of the last line received; resumes the stream from that second (within the hour).
|
||||
schema: { type: string }
|
||||
responses:
|
||||
'200':
|
||||
description: An event stream of build log lines.
|
||||
description: >-
|
||||
An event stream of build log lines (`id:` + `data:` per line), ended by
|
||||
`event: revoked` when the caller is no longer staff.
|
||||
content:
|
||||
text/event-stream:
|
||||
schema: { type: string }
|
||||
|
||||
+22
-1
@@ -1238,6 +1238,26 @@ All four mandated metrics have real producers; scrape them when triaging:
|
||||
- `felis_reaper_worlds_deleted_total` — increments only after a world PVC is
|
||||
actually deleted post-backup (§10); a spike here means worlds crossed the 15d
|
||||
idle line — cross-check that join events are flowing (§10 risk vectors).
|
||||
- `felis_http_requests_total{face,method,route,code}` and
|
||||
`felis_http_request_duration_seconds{face,route}` — every API request, by the
|
||||
route pattern it matched (`/api/v1/servers/{name}/status`, never the raw
|
||||
path; `unmatched` for a path no route serves). `face` is `internal` or
|
||||
`external`. Log streams count as requests but stay out of the latency
|
||||
histogram. A rise in `code=~"5.."` on one route narrows a failure to one
|
||||
handler; `route="unmatched"` rising is someone scanning.
|
||||
- The Go runtime and process series (`go_*`, `process_*`) of `felis-api`:
|
||||
goroutines, heap, open file descriptors. Goroutines that climb without
|
||||
falling back usually mean streams or uploads that never end.
|
||||
|
||||
The API also writes one access-log line per request to its log, in logfmt:
|
||||
`face`, `method`, `route`, `path`, `status`, `duration_ms`, `bytes`,
|
||||
`request_id` (the id in every error envelope) and `principal` (the user id,
|
||||
once signed in). Successful probes and scrapes are left out.
|
||||
|
||||
```bash
|
||||
kubectl -n felis logs deploy/felis-api | grep 'msg=request' | grep 'status=5'
|
||||
kubectl -n felis logs deploy/felis-api | grep 'request_id=<id from the error>'
|
||||
```
|
||||
|
||||
### Scraping
|
||||
|
||||
@@ -1250,7 +1270,8 @@ annotated Service endpoints picks them up as is.
|
||||
`felis_build_info{component="operator"}`, and controller-runtime's
|
||||
`controller_runtime_reconcile_*` / `workqueue_*` series.
|
||||
- `felis-api` internal face `:8081/metrics` (Service `felis-api-internal`) —
|
||||
`felis_build_info{component="api"}`,
|
||||
`felis_build_info{component="api"}`, the `felis_http_*` request series, the
|
||||
`go_*`/`process_*` runtime series,
|
||||
`felis_image_build_failures_total`, and the sign-in series of §17
|
||||
(`felis_mail_total`, `felis_rate_limited_total`,
|
||||
`felis_auth_otp_lockouts_total`, `felis_auth_failures_total`,
|
||||
|
||||
Reference in new issue
Block a user