Forward CA and proxy env vars into the SRT sandbox

What does this MR do and why?

  • The Duo Agent Platform SRT sandbox runs $SANDBOX_UNSHARE env -i <allowlist> srt --settings ... duo run .... env -i drops every environment variable not on the allowlist, including NODE_EXTRA_CA_CERTS and the proxy variables.
  • Self-managed instances with a private CA or a proxy fail TLS inside the sandbox: customers see self-signed certificate / SELF_SIGNED_CERT_IN_CHAIN / UNABLE_TO_VERIFY_LEAF_SIGNATURE, and Duo CLI logs NODE_EXTRA_CA_CERTS: (not set).
  • Adds a FORWARDED_ENV_VARS constant in ee/lib/gitlab/duo_workflow/sandbox.rb (NODE_EXTRA_CA_CERTS, HTTP_PROXY, HTTPS_PROXY, NO_PROXY, http_proxy, https_proxy, no_proxy).
  • A new build_forwarded_env_vars_flags renders these as ${VAR+VAR="$VAR"} and appends them to the sandbox run line, so a variable is forwarded only when it is set.
  • The existing allowlist form (VAR="$VAR") creates an unset variable as an empty string. SRT resolves its upstream proxy as HTTPS_PROXY ?? https_proxy ?? HTTP_PROXY, and ?? treats "" as set, so an empty HTTPS_PROXY would hide a lowercase https_proxy and SRT would not chain to the proxy. SRT then points the child process at its own local proxy.
  • NODE_TLS_REJECT_UNAUTHORIZED is intentionally not forwarded: setting it to 0 turns off TLS verification.
  • The nono path (behind the duo_workflow_nono_sandbox feature flag) builds the same env -i prefix, so it gets the same forwarding on its pre-check and run lines. nono only allows reads of granted paths, so it also gets ${NODE_EXTRA_CA_CERTS:+--read-file "$NODE_EXTRA_CA_CERTS"}, which adds a grant only when the variable is set. nono warns and skips a grant for a missing file, and it replaces the proxy variables with its own local proxy, so on that path only the CA forwarding takes effect.
  • Updates doc/user/duo_agent_platform/troubleshooting.md: lists the forwarded variables, says to set them in the runner config.toml environment directive or export them in the agent-config.yml setup_script, and removes the incorrect sentence that setup_script is not filtered by the sandbox. Custom CI/CD variables are not available in DAP jobs, so the docs do not suggest them.
  • Specs cover the SRT and nono run lines and run the generated script under bash with fake srt / unshare / nono: set variables arrive, unset ones stay unset, an unrelated FOOBAR is stripped, a value containing $(...) is passed literally and not executed, and the nono CA grant arrives as a single path and is absent when unset.
  • The executing spec also closes a testability gap behind the stalled !243689 (comment 3531597138) check. There was no way to observe the sandboxed environment: a debug command added to executor_cli_command lands on the same shell line as env -i, so the job shell expands its variables first, and a && in it runs the rest outside srt. The spec and the validation below both read the environment from inside the sandbox.
  • The AppSec shell-injection concern raised on that MR does not apply: an expanded "$VAR" is not re-scanned for $(...) or backticks.

References

Closes #604795 (closed)

Supersedes !243689 (closed)

Screenshots or screen recordings

N/A, backend and docs changes only.

How to set up and validate locally

This reproduces the customer setup: a GitLab instance behind a private CA, and a runner that sets NODE_EXTRA_CA_CERTS. It works on GDK with Colima, because a privileged Docker runner can create the SRT sandbox there.

  1. Serve GDK over HTTPS with an mkcert certificate. In gdk.yml, enable duo_workflow.debug so the Duo CLI logs its TLS settings, then run gdk reconfigure && gdk restart:

    port: 3443
    https:
      enabled: true
    nginx:
      enabled: true
      ssl:
        certificate: gdk.test.pem
        key: gdk.test-key.pem
    duo_workflow:
      debug: true
  2. Make the CA available to job containers. The GDK runner mounts ~/.docker/certs.d at /etc/docker/certs.d:

    cp "$(mkcert -CAROOT)/rootCA.pem" ~/.docker/certs.d/gdk-ca.pem
  3. In gitlab-runner-config.toml, under [[runners]], set the variable the way a customer would, then run gdk restart runner:

    environment = ["NODE_EXTRA_CA_CERTS=/etc/docker/certs.d/gdk-ca.pem"]
  4. Run the same flow three times, for example by mentioning a flow's service account on an issue, and open each workload job log:

    Run Branch [runners.docker] privileged Expected in the job log
    A (control) master false SRT found but can't create sandbox, the Duo CLI Network/TLS configuration block shows the CA path, and the flow runs.
    B (bug) master true SRT sandbox test successful, then NODE_EXTRA_CA_CERTS: (not set) and unable to verify the first certificate (UNABLE_TO_VERIFY_LEAF_SIGNATURE). The job fails.
    C (fix) this branch true SRT sandbox test successful, the srt line includes ${NODE_EXTRA_CA_CERTS+...}, the CLI shows the CA path, the websocket connects, and the flow runs.

    Run gdk restart runner after each privileged change.

  5. git fetch / git push over HTTPS fail on all three runs, including A. That is separate from this MR: for Docker executors the runner only writes http.<host>.sslCAInfo into the helper container's global git config, so git in the build container does not trust the CA.

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

</🤖>

Edited by Sebastian Rehm

Merge request reports

Loading
Loading