Skip to content

chore(secrethub): bump tackle to v0.4.1 - #1158

Open
loadez wants to merge 4 commits into
mainfrom
bump/tackle-v0.4.0-secrethub
Open

loadez wants to merge 4 commits into
mainfrom
bump/tackle-v0.4.0-secrethub

Conversation

@loadez

@loadez loadez commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

No description provided.

@github-project-automation github-project-automation Bot moved this to Backlog in Roadmap Aug 13, 2026
@loadez
loadez force-pushed the bump/tackle-v0.4.0-secrethub branch from 0f6d3ca to ccac500 Compare August 14, 2026 12:03
@loadez
loadez marked this pull request as ready for review August 14, 2026 12:17

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ccac500413

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread secrethub/.mix-audit.txt
# The OIDC HTTPServer is started with `scheme: :http` and no protocol_options enabling h2c
# (lib/secrethub/application.ex openid_connect_services/0) - Cowboy serves HTTP/1.1 only here,
# so the HTTP/2-specific :scheme pseudo-header parsing path is never invoked.
GHSA-q8x4-x7mp-5vg2

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Do not suppress the h2c advisory while h2c remains enabled

For clients able to reach the pod or its ClusterIP directly, this allowlist hides a reachable unauthenticated DoS: scheme: :http selects a cleartext transport but does not make Cowboy HTTP/1.1-only, because clear listeners accept HTTP/2 prior knowledge/h2c by default. I checked Secrethub.Application.openid_connect_services/0, which supplies only options: [port: port], and the Helm service exposes that listener without configuring protocol_options: [protocols: [:http]]; therefore crafted HTTP/2 :scheme values can still reach the vulnerable plug_cowboy 2.5.2 path. Upgrade plug_cowboy or explicitly disable HTTP/2 before ignoring this advisory.

Useful? React with 👍 / 👎.

@loadez
loadez requested a review from adbatista as a code owner August 17, 2026 12:05
adbatista
adbatista previously approved these changes Aug 17, 2026
skipi
skipi previously approved these changes Aug 21, 2026
@skipi
skipi self-requested a review August 21, 2026 11:25
@skipi

skipi commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

Four notes. The service_per_exchange change fixes a real misrouting bug, but two things about it need sorting before this deploys, and the audit-file rationale has a hole.

1. service_per_exchange: true renames live queues, and nothing cleans up the old ones.

Today both routes share one queue, secrethub.secret_destroyer.deleted. After this change they become secrethub.secret_destroyer.project_exchange.deleted and secrethub.secret_destroyer.organization_exchange.deleted, each with new .dead and .delay siblings and a new service exchange. Tackle only ever creates — it never unbinds or deletes. So on deploy: anything sitting in the old queue at cutover is never consumed again (dropped secret-destroy events for orgs and projects), and the old service exchange stays bound to project_exchange and organization_exchange, so the abandoned queue keeps receiving every delete event indefinitely with no consumer attached. On a rolling deploy, old and new pods also consume from different queues for the duration of the overlap.

Could you add the cutover steps to the PR description — drain, then unbind and delete the old exchange/queue trio post-deploy? The code change is right; it just isn't self-sufficient.

2. The justification comment is factually wrong and will mislead the next reader.

The comment says "Without service_per_exchange: true, tackle >= 0.4 derives the same queue name … for both routes". service_per_exchange has existed since tackle v0.2.2 (commit e3decb0) and defaults to false in every version through v0.4.1 — see lib/tackle/multiconsumer.ex:46 and service_name/3. v0.4.1 without the flag derives queue names identically to the tag this PR replaces. The route collision is pre-existing and version-independent: a genuine bug, but not one this bump introduced. As written, the comment tells whoever hits a misrouting incident that rolling tackle back would fix it, which it would not.

3. The new test cannot fail.

owner_deleted_consumer_test.exs calls Tackle.Multiconsumer.service_name/3 with hardcoded string literals and asserts that the true and false variants differ. It never references OwnerDeletedConsumer — remove the service_per_exchange: true line from the consumer and both new tests still pass. Asserting against the consumer's own derived queue names (or its generated consumer module names) would actually guard the behaviour the comment describes.

4. The h2c suppression premise is inverted.

GHSA-q8x4-x7mp-5vg2 is justified on the grounds that the OIDC server runs with scheme: :http and no protocol_options, therefore "Cowboy serves HTTP/1.1 only here, so the HTTP/2-specific :scheme pseudo-header parsing path is never invoked." Reading cowboy source (2.7.0 and 2.12.0, which bracket the locked 2.9.0 — identical in both):

  • cowboy_clear.erl:43Protocol = case maps:get(protocols, Opts, [http2, http]). The default protocol set already includes HTTP/2; nothing has to be enabled.
  • cowboy_http.erl:487-490 — the PRI * HTTP/2.0 connection preface is accepted directly.
  • cowboy_http.erl:865-873Upgrade: h2c is handled too.
  • cowboy_http.erl:884-894 — and this is the part that inverts the argument: prior-knowledge HTTP/2 is accepted only when Transport:secure() is false, and explicitly rejected on TLS with "Clients that support HTTP/2 over TLS MUST use ALPN". scheme: :http is therefore precisely the configuration under which cowboy will speak h2c, not the one that prevents it.

The tainted value does reach the vulnerable code unvalidated: cowboy_http2.erl:498,516 writes the client-supplied :scheme pseudo-header into the Req map, ensure_port/2 at :546-548 has a catch-all clause with no scheme validation, cowboy_req.erl:188 returns it, and the locked plug_cowboy 2.5.2 does scheme: String.to_atom(:cowboy_req.scheme(req)) at lib/plug/cowboy/conn.ex:27 — the unbounded atom creation. The 2.8.1 fix replaces that with a two-clause case over the "http"/"https" literals.

In fairness on blast radius, and this is not in the file either: helm-chart/templates/emissary-ingress/mappings.yaml:766-769 fronts this port publicly (hostname: "*", prefix /.well-known/secrethub-openid-connect-http:5000), but that Mapping sets no allow_upgrade and no protocol_version, so Envoy speaks HTTP/1.1 upstream and an internet-borne preface is not forwarded verbatim. Residual exposure is in-cluster (any pod reaching ClusterIP:5000 or the pod IP directly) plus on-prem installs with different fronting. Real, but not internet-facing.

Either way the fix is one line: protocol_options: [protocols: [:http]] on the child spec, which cowboy honours at cowboy_clear.erl:43. That makes the suppression true as written, instead of true by accident.

Also worth noting: this service still locks cowboy 2.9.0, cowlib 2.11.0, plug 1.13.6 and plug_cowboy 2.5.2, and mix.lock is otherwise untouched here — so several of the advisories being suppressed have fixed versions available that sibling PRs in this same batch chose to upgrade to instead. Worth settling which disposition is the intended policy, so the audit trail doesn't record two different verdicts on the same advisory from the same day.

@loadez
loadez force-pushed the bump/tackle-v0.4.0-secrethub branch from f327784 to f887a03 Compare August 26, 2026 11:17
@loadez
loadez force-pushed the bump/tackle-v0.4.0-secrethub branch from f887a03 to 3158756 Compare September 9, 2026 15:32
@loadez
loadez force-pushed the bump/tackle-v0.4.0-secrethub branch from 065ba3d to 40b4ac1 Compare September 10, 2026 13:37
adbatista
adbatista previously approved these changes Sep 11, 2026
@adbatista adbatista changed the title chore(secrethub): bump tackle to v0.4.0 chore(secrethub): bump tackle to v0.4.1 Sep 11, 2026
…revent org/project delete misrouting under tackle v0.4.1

OwnerDeletedConsumer has two routes with the same routing key "deleted"
on different exchanges (project_exchange, organization_exchange).
ex-tackle >= v0.2.2 made service_per_exchange opt-in and default false,
so both routes derive the same queue name and end up as competing
consumers on one queue, misrouting org-delete and project-delete
secret-cleanup events. This bump to tackle v0.4.1 exposes the bug.

Sets service_per_exchange: true so each route gets its own queue,
mirroring the identical fix on ee/pre_flight_checks. Also adds tests
proving the two routes now resolve to distinct queue names, and that
they would collide without the flag.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

3 participants