Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

:bug: eventing: no owned policy for who may read which alert topic — alert authorization lives in ntfy's auth db

未关闭
#899 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 6 天内回复

还没有人认领这个 Issue。

评估

难度
3/5
预计耗时
半天
新手友好度
62/100
Issue 类型
文档
描述清晰度
基本清楚
活跃度
活跃
技术栈
python

调研方向

Start with DESIGN_PHASE3.md §4.1–§4.3 and §8.7 (the documented-pattern precedent), then skim eventbridge/registry.py's User dataclass and NtfyPublisher to confirm the issue's claims about where authorization does and does not live. The concrete deliverable is the first checkbox: one added paragraph in DESIGN_PHASE3.md §4 stating that alert authorization is ntfy's, not ours, and that per-user revocation does not exist — done when the doc reads true against the code. The second checkbox (a test that provisioned ACLs match the registry, or an explicit note that none exists) is the stretch goal; the third (choosing option A vs B) is a maintainer decision, not yours.

由索引模型根据 Issue 内容生成。

描述

Summary

There is no policy object, anywhere in this system, that says who may read which
notification topic
. What separates two users' alerts today is possession of an
unguessable topic name, optionally backed by ACLs in ntfy's SQLite auth database,
provisioned by hand with kubectl exec. The authorization decision for alerts is not one
we own, evaluate, or can audit.

This is not a claim that the design is wrong — DESIGN_PHASE3.md §4.1–§4.3 is explicit
that the name is a capability and that auth-default-access: deny-all is "the control
that makes this isolation rather than obscurity". It is a request to record the gap as a
gap, because no document currently states it plainly and the question comes up
immediately when anyone asks how permissions work.

What exists, and what enforces it

Three layers, and they are not equally real:

Layer Where it lives Enforced by
Who may submit work EB_ALLOWED_USERS (env) or EB_USER_REGISTRY_PATH (ConfigMap) our code — ghauth.is_allowed, registry.lookup
Which tenant a request belongs to derived userkey → topics, per-user store our code (resolve_caller, store_registry)
Who may read which alert topic ntfy's auth db ntfy — not us

Verified: grep for ACL / subscription-authorization logic across eventbridge/*.py and
shared/*.py returns nothing. eventbridge/handlers.py has no ntfy references at all.
The only notification-side code is NtfyPublisher, which publishes, and still reads a
single self._cfg.topic for every event — Phase 3 §4.2's per-user _topic_for is design
only.

So on ntfy.sh, the topic name is the entire access control: tenancy.ntfy_topic gives
130 bits of unguessability, and anyone who learns the name reads the stream — the prompt,
the assistant's reply and the stats footer, because compose_body deliberately packs all
of it in so a phone that cannot reach EventBridge still shows the result. §4.1 says this
outright ("the feature that makes the notification good is what makes the leak complete").

What the registry does and does not model

eventbridge/registry.py:64 is the closest thing to a role model:

@dataclass(frozen=True)
class User:
    issuer: str
    userid: str
    userkey: str
    tier: str = TIER_SHARED      # shared | isolated
    agent: str = ""
    limits: Limits = ...

tier is a placement decision (shared vs dedicated runner), not a permission. agent
selects an AgentSpec. limits is rate and size. Nothing expresses "principal P may
read topic T"
— because the model is implicitly one-to-one: a user reads their own
topic and no one else's, and that relation is re-derived from the userkey rather than
stored.

That is a defensible MVP. It becomes insufficient the moment any of these is wanted, and
all three are ordinary asks:

  • An on-call rotation seeing another user's approval requests (directly relevant to
    #897, where a sleeping approver is the designed-for failure case — §6 exists entirely
    because of it).
  • A team topic several people read.
  • Revocation. Today, removing someone's access to alerts means rotating
    EB_NTFY_TOPIC_SECRET_PATH, which rotates every user's topic at once (§4.2 names
    this as a feature for leak response, and it is — but it is the only lever, so
    per-user revocation does not exist).

Why "it's in ntfy's ACLs" is not sufficient as an answer

Even in incluster mode, where ACLs are real:

  1. The grants live outside the declared state. §4.3's own caveat: "Losing the PVC
    loses the ACLs, not just the cache — the auth database lives there."
    k8s_tenant.py apply --all is the recovery path, which makes the registry the source
    of truth for provisioning while the enforcement point is a cache of it that can
    silently drift.
  2. Provisioning is imperative and not idempotent. §4.3: the commands "only work on the
    server itself — hence kubectl exec", and "user add fails if the user exists". The
    declarative alternative is explicitly rejected because removing a user from the config
    file deletes them from the database.
  3. Nothing verifies the grants match the registry. There is no reconcile loop and no
    test. An ACL that was never applied, or applied to the wrong literal topic, fails
    open (the user reads nothing → looks like a broken notification) or, if a prefix
    grant is used by mistake, fails catastrophically (§4.3 warns: a kev1-* read grant
    "would hand them everyone's notifications").
  4. We cannot audit it. There is no endpoint, page or log line that answers "who can
    currently read alice's alerts?" The answer is in a SQLite file in a pod.

Point 3 is the one that matches this repository's own pattern: DESIGN_PHASE2.md §8.5.3
asks what happens when a control is half-configured, and here the half-configured case is
silent, in the direction of either no notifications or everyone's notifications.

Suggested direction (not a prescription)

Two shapes, and they are genuinely different bets:

A. Keep ntfy as the transport, own the policy. Add a notification-access relation to
the registry — subjects, topics, and a verb — and make k8s_tenant.py reconcile ntfy's
ACLs from it rather than emit missing grants once. Add a read-only endpoint that reports
the effective grants so the question is answerable. Cheap, incremental, and leaves
enforcement in ntfy.

B. Stop treating the ntfy topic as the authorization boundary. Notifications are
fetched through EventBridge (which already authenticates, already knows the owner, and is
already the PEP per §2.7), and ntfy carries only a content-free wake-up. Alert access then
becomes an owner-scoped read like every other read in §6.2, enforced by code we test.
Bigger change; removes the whole category.

B composes better with #897's approval flow, because an approval decision is a privileged
action and §5.4 already has to bind it to a user and a tool input — at which point the
notification channel being merely a transport is the simpler story. But A is a real option
and the cheaper one, and the choice should be deliberate rather than defaulted.

What would make this concrete

  • State the gap in DESIGN_PHASE3.md §4 — one paragraph saying alert authorization is
    ntfy's, not ours, and that per-user revocation does not exist. §8.7's pattern: leave
    the design as written, add what the code does.
  • A test that the provisioned ACLs match the registry, or an explicit note that none
    exists.
  • Decide A vs B before EB_TENANCY_MODE=multi is recommended for anything real.

Scope note

Phase 3, not Phase 4, and filed separately from #897 for the same reason as the sibling
issue: the finding belongs with the code it describes. #897 depends on this — its §3
enrollment hands a user a topic whose access control is exactly what this issue is about,
and its §3.5 already records the narrower bound ("enrollment proves the GitHub account
asked for a topic [...] not that this topic reaches only that person's phone").

Verification

Statements about what the code does were checked against eventing/ at dc1d432:
NtfyPublisher publishes to self._cfg.topic with no per-user routing; no ACL or
subscription-authorization logic exists in eventbridge/ or shared/; registry.User
carries no permission field. Statements about ntfy's behaviour and the provisioning
caveats are quoted from DESIGN_PHASE3.md §4.3, which records them as design, and were
not re-verified against a running ntfy — no cluster was involved.

主要语言
Python
星标
11
派生
42
平均合并
4 天 11 小时
30 天内合并 PR
40

环境准备

  • 没有 Dockerfile 或 Docker Compose 文件
  • 没有 Pull Request 模板
  • 阅读贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

rossoctl/examples 的其他 Issue

查看 rossoctl/examples 的全部 Issue

相似的 Issue

更多 Python Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。