Skip to content

feat(integrations): CTFd plugin for Dploy environments - #43

Open
AYDEV-FR wants to merge 1 commit into
mainfrom
feat/ctfd-dploy-plugin
Open

feat(integrations): CTFd plugin for Dploy environments#43
AYDEV-FR wants to merge 1 commit into
mainfrom
feat/ctfd-dploy-plugin

Conversation

@AYDEV-FR

Copy link
Copy Markdown
Owner

What

Adds a CTFd plugin (integrations/ctfd_dploy/) that lets players start / stop / extend / check an on-demand Dploy environment directly from the challenge view.

It pairs with the companion ctfd-oidc-provider plugin: CTFd is the OIDC Provider of Dploy, so a CTFd-issued token is exactly what Dploy's API trusts.

How it works

No custom challenge type. Any standard challenge becomes a Dploy challenge when its Connection Info is a Dploy run URL, e.g. https://dploy.example.com/run/podinfo. The plugin parses the API base + template server-side and injects a Start/Refresh/Extend/Stop panel into the challenge modal. The detection pattern is configurable (DPLOY_URL_PATTERN).

Auth (BFF / Backend-For-Frontend):

  • The browser only calls same-origin proxy routes (/plugins/ctfd_dploy/challenges/<id>/{start,status,extend,stop}), gated by CTFd session + CSRF.
  • Server-side, the plugin obtains a CTFd-issued id_token for the player via a trusted, auto-approved OIDC auth-code+PKCE bounce against CTFd's own provider, caches it in the Flask session, and forwards it to Dploy as a Bearer token. The token never reaches JavaScript.
  • Connection URL → (base, template) is resolved only from the challenge's stored connection_info, never a client value (SSRF-safe).

Why "no auth problems": the OAuth client id is dploy (matches Dploy's JWT_AUDIENCE=dploy) and the profile scope is requested (Dploy's JWT_USERNAME_CLAIM=name), all against the same CTFd issuer + JWKS. No changes to Dploy itself.

Test stack

integrations/testing/ adds a docker compose up --build stack: CTFd with both plugins + a mock Dploy API that validates tokens against CTFd's JWKS exactly like real Dploy (RS256, iss/aud, name claim). Exercises the full auth + proxy + UI path without a Kubernetes cluster. See integrations/testing/README.md.

Notes / not yet verified

  • Targets CTFd 3.6/3.7 core theme; the panel is injected by observing the challenge modal (findModalBody selectors are defensive). A heavily customized theme may need a tweak.
  • Adds no DB tables / models — only reads the standard connection_info field.
  • Not yet run against live Docker in this environment; Python compiles and dploy.js passes node --check.

https://claude.ai/code/session_01M4Dd5oASBGHwr6ts6wTnYn

Add a CTFd plugin that lets players start/stop/extend/check an on-demand
Dploy environment directly from the challenge view.

Rather than a custom challenge type, any standard challenge becomes a Dploy
challenge when its Connection Info is a Dploy run URL
(e.g. https://dploy.example.com/run/<template>); the plugin parses the API
base + template from it server-side and injects a Start/Refresh/Extend/Stop
panel into the challenge modal.

Auth uses a BFF model: the browser only calls same-origin proxy routes; the
plugin obtains a CTFd-issued id_token for the player via a trusted,
auto-approved OIDC auth-code+PKCE bounce against CTFd's own provider (the
companion ctfd-oidc-provider plugin), caches it server-side, and forwards it
to Dploy as a Bearer token. Aligning the client id with Dploy's JWT_AUDIENCE
(dploy) and requesting the profile scope (name claim) means no auth problem
appears.

Also includes a docker-compose test stack: CTFd with both plugins plus a mock
Dploy API that validates tokens against CTFd's JWKS exactly like real Dploy,
exercising the full auth + proxy + UI path without a Kubernetes cluster.

Claude-Session: https://claude.ai/code/session_01M4Dd5oASBGHwr6ts6wTnYn
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant