Dagger stunnel #316

Closed
opened 2026-05-28 03:15:10 +00:00 by guettli · 2 comments
guettli commented 2026-05-28 03:15:10 +00:00 (Migrated from codeberg.org)

Show link to code where this gets printed:

Plain TCP Dagger connection succeeded — no TLS stunnel needed.

Why can that happen?

I thought this is executed inside a container, and can not happen.

Show link to code where this gets printed: > Plain TCP Dagger connection succeeded — no TLS stunnel needed. Why can that happen? I thought this is executed inside a container, and can not happen.
guettlibot commented 2026-05-28 03:59:38 +00:00 (Migrated from codeberg.org)

Implementation Plan: Remove Plain-TCP Dagger Fallback

Where the message is printed

scripts/setup_dagger_remote.sh, lines 35–48:

https://codeberg.org/guettli/sharedinbox/src/branch/main/scripts/setup_dagger_remote.sh#L35-L48

The block tries to run dagger version over a plain TCP URL; if that call exits 0, it prints the message and exits 0 — skipping stunnel entirely.


Why plain TCP can succeed

The script runs on the self-hosted Forgejo runner, which lives on the same host as the Dagger engine. The engine is configured to listen on tcp://0.0.0.0:8080 (see DAGGER.md). When the runner job executes inside a container with host networking (or with the host's port reachable via Docker bridge), it can reach the Dagger engine over plain TCP on the local/internal interface — without going through the stunnel TLS path at all.

So the author's intuition is half-right: the code is executed inside a (Forgejo runner) container, but that container has network access to the host's Dagger engine port, making plain TCP reachable despite the container boundary.

In short: plain TCP succeeds because the runner and the engine are co-located on the same machine and the engine listens on all interfaces.


Problem with the current design

The plain-TCP fallback was likely added to support a plain-proxy configuration that exists on the host side. However:

  1. It silently bypasses TLS — the stunnel setup (and all the DAGGER_CA_CERT / DAGGER_CLIENT_CERT / DAGGER_CLIENT_KEY secrets) are never exercised when co-located, so those secrets are useless in practice.
  2. The Codeberg job never tests the stunnel path — if stunnel credentials rotate or break, CI won't catch it until a remote runner (not co-located) tries to connect.
  3. No mutual authentication over plain TCP — any process that can reach the engine port gets access.

Proposed implementation

File to change: scripts/setup_dagger_remote.sh

Option A — Remove the plain-TCP block entirely (recommended)

Delete lines 34–49 (the "2a. Try plain TCP" block). Let the script always proceed to the stunnel path.

This forces every CI run to go through stunnel, keeping the TLS certs exercised and maintaining mutual authentication.

Risk: If the host-side Dagger engine is not wrapped by a TLS-terminating proxy (i.e., it really is plain TCP on port 8774/8080), the stunnel step will fail. In that case either:

  • The engine's listening address must be changed to require TLS, or
  • The host must expose a stunnel server in front of the engine.

Option B — Guard plain TCP with an explicit env-var opt-in

If plain-TCP access is intentional for local/dev scenarios, gate it behind a flag:

if [ "${DAGGER_ALLOW_PLAIN_TCP:-false}" = "true" ]; then
  # ... existing plain-TCP block ...
fi

CI secrets never set DAGGER_ALLOW_PLAIN_TCP, so the stunnel path is always used in CI. A developer on the same host can set the var explicitly.


Workflow files to audit

Check whether any of the following also need updating (they all call setup_dagger_remote.sh):

  • .forgejo/workflows/ci.yml — lines 59–65
  • .forgejo/workflows/deploy.yml — lines 104–110, 143–149, 185–191, 227–233

The cleanup step (rm -rf /tmp/dagger-tls /tmp/stunnel-dagger.conf /tmp/stunnel.pid) remains valid in either option.


Open questions

  1. Does the Dagger engine on the host already sit behind a TLS-terminating stunnel/proxy? If yes, Option A is safe to apply immediately. If no, the host config must be updated first to avoid breaking CI.
  2. Is plain-TCP access intentional for local development? If so, Option B is the safer choice.
  3. Should the script enforce that TLS cert env vars are non-empty before proceeding? Currently the stunnel path will silently write empty files if the secrets are unset; adding a check would make failures explicit.
## Implementation Plan: Remove Plain-TCP Dagger Fallback ### Where the message is printed `scripts/setup_dagger_remote.sh`, lines 35–48: https://codeberg.org/guettli/sharedinbox/src/branch/main/scripts/setup_dagger_remote.sh#L35-L48 The block tries to run `dagger version` over a plain TCP URL; if that call exits 0, it prints the message and exits 0 — skipping stunnel entirely. --- ### Why plain TCP can succeed The script runs on the **self-hosted Forgejo runner**, which lives on the **same host** as the Dagger engine. The engine is configured to listen on `tcp://0.0.0.0:8080` (see `DAGGER.md`). When the runner job executes inside a container with host networking (or with the host's port reachable via Docker bridge), it can reach the Dagger engine over plain TCP on the local/internal interface — without going through the stunnel TLS path at all. So the author's intuition is half-right: the code *is* executed inside a (Forgejo runner) container, but that container has network access to the host's Dagger engine port, making plain TCP reachable despite the container boundary. In short: **plain TCP succeeds because the runner and the engine are co-located on the same machine and the engine listens on all interfaces.** --- ### Problem with the current design The plain-TCP fallback was likely added to support a plain-proxy configuration that exists on the host side. However: 1. **It silently bypasses TLS** — the stunnel setup (and all the `DAGGER_CA_CERT` / `DAGGER_CLIENT_CERT` / `DAGGER_CLIENT_KEY` secrets) are never exercised when co-located, so those secrets are useless in practice. 2. **The Codeberg job never tests the stunnel path** — if stunnel credentials rotate or break, CI won't catch it until a remote runner (not co-located) tries to connect. 3. **No mutual authentication over plain TCP** — any process that can reach the engine port gets access. --- ### Proposed implementation **File to change:** `scripts/setup_dagger_remote.sh` #### Option A — Remove the plain-TCP block entirely (recommended) Delete lines 34–49 (the "2a. Try plain TCP" block). Let the script always proceed to the stunnel path. This forces every CI run to go through stunnel, keeping the TLS certs exercised and maintaining mutual authentication. **Risk:** If the host-side Dagger engine is *not* wrapped by a TLS-terminating proxy (i.e., it really is plain TCP on port 8774/8080), the stunnel step will fail. In that case either: - The engine's listening address must be changed to require TLS, or - The host must expose a stunnel server in front of the engine. #### Option B — Guard plain TCP with an explicit env-var opt-in If plain-TCP access is intentional for local/dev scenarios, gate it behind a flag: ```bash if [ "${DAGGER_ALLOW_PLAIN_TCP:-false}" = "true" ]; then # ... existing plain-TCP block ... fi ``` CI secrets never set `DAGGER_ALLOW_PLAIN_TCP`, so the stunnel path is always used in CI. A developer on the same host can set the var explicitly. --- ### Workflow files to audit Check whether any of the following also need updating (they all call `setup_dagger_remote.sh`): - `.forgejo/workflows/ci.yml` — lines 59–65 - `.forgejo/workflows/deploy.yml` — lines 104–110, 143–149, 185–191, 227–233 The cleanup step (`rm -rf /tmp/dagger-tls /tmp/stunnel-dagger.conf /tmp/stunnel.pid`) remains valid in either option. --- ### Open questions 1. **Does the Dagger engine on the host already sit behind a TLS-terminating stunnel/proxy?** If yes, Option A is safe to apply immediately. If no, the host config must be updated first to avoid breaking CI. 2. **Is plain-TCP access intentional for local development?** If so, Option B is the safer choice. 3. **Should the script enforce that TLS cert env vars are non-empty before proceeding?** Currently the stunnel path will silently write empty files if the secrets are unset; adding a check would make failures explicit.
guettlibot commented 2026-05-28 04:00:01 +00:00 (Migrated from codeberg.org)

Planning complete. To resume this session:

claude --resume 9e097e4e-8bc7-4878-ba81-3a09240312e8 --dangerously-skip-permissions
Planning complete. To resume this session: ``` claude --resume 9e097e4e-8bc7-4878-ba81-3a09240312e8 --dangerously-skip-permissions ```
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: guettli/sharedinbox#316