Daytona Integration
This guide walks through running iron-proxy inside Daytona sandboxes. You will build a custom Docker image with iron-proxy pre-installed, then use Daytona’s declarative builder to launch sandboxes with egress control already configured.
Daytona blocks most egress traffic by default depending on your billing tier. iron-proxy gives you granular control over which domains are accessible, TLS interception, and request logging on top of Daytona’s built-in restrictions.
How It Works
The setup uses a multi-stage Docker image that bundles iron-proxy into a Debian base. When the container starts, the entrypoint script:
- Generates a CA certificate for TLS interception and installs it into the system trust store.
- Configures iptables rules to block all non-loopback egress from non-root processes.
- Points DNS at iron-proxy so all domain resolution resolves to the proxy’s listen address.
- Starts iron-proxy in the background.
- Removes the
daytonauser’s sudo access so workloads cannot escalate to root. - Hands off to whatever command you pass in.
Because DNS resolves all domains to iron-proxy’s listen address and iptables blocks all non-loopback egress from non-root processes, processes inside the sandbox cannot bypass the proxy. The container runs as the unprivileged daytona user, and sudo access is revoked after setup completes.
Prerequisites
- A Daytona account with API access
- Python 3.10+
- The Daytona Python SDK:
pip install daytona
Setup
Create the iron-proxy Configuration
Save this as iron-proxy.yaml. This is the configuration file iron-proxy will use at runtime.
dns:
listen: ":53"
proxy_ip: "127.0.0.1"
upstream_resolver: "8.8.8.8:53"
proxy:
http_listen: ":80"
https_listen: ":443"
tls:
ca_cert: "/etc/iron-proxy/ca.crt"
ca_key: "/etc/iron-proxy/ca.key"
transforms:
- name: allowlist
config:
warn: true
domains:
- "*"
log:
level: "info"A few things to note about this configuration:
dns.proxy_ipis set to127.0.0.1because iron-proxy runs inside the same sandbox as your workloads.dns.upstream_resolveruses8.8.8.8:53. Change this if your network requires a different upstream resolver.warn: truemeans non-allowlisted requests are logged but not blocked. Set tofalseto enforce the allowlist.domains: ["*"]allows all domains. Replace this with specific domains to restrict egress.
For the full set of configuration options, see the configuration reference.
Create the Entrypoint Script
Save this as entrypoint.sh. It runs at container startup to configure networking and start iron-proxy.
#!/usr/bin/env bash
set -uo pipefail
GUARD_FILE="/var/run/iron-proxy-setup-done"
if [ -f "$GUARD_FILE" ]; then
exec "$@"
fi
# Generate CA cert/key for TLS MITM and install into system trust store
if [ ! -f /etc/iron-proxy/ca.crt ]; then
sudo openssl genrsa -out /etc/iron-proxy/ca.key 4096 2>/dev/null
sudo openssl req -x509 -new -nodes \
-key /etc/iron-proxy/ca.key \
-sha256 -days 3650 \
-subj "/CN=iron-proxy CA" \
-addext "basicConstraints=critical,CA:TRUE" \
-addext "keyUsage=critical,keyCertSign" \
-out /etc/iron-proxy/ca.crt 2>/dev/null
sudo cp /etc/iron-proxy/ca.crt /usr/local/share/ca-certificates/iron-proxy.crt
sudo update-ca-certificates 2>/dev/null
fi
# Block non-root, non-loopback egress
sudo iptables -A OUTPUT -o lo -j ACCEPT
sudo iptables -A OUTPUT -m owner --uid-owner root -j ACCEPT
sudo iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
sudo iptables -A OUTPUT -j REJECT --reject-with icmp-port-unreachable
# Point DNS at iron-proxy
echo "nameserver 127.0.0.1" | sudo tee /etc/resolv.conf > /dev/null
# Start iron-proxy in background
sudo --preserve-env setsid bash -c '/usr/local/bin/iron-proxy -config /etc/iron-proxy/config.yaml &>/var/log/iron-proxy.log' &
# Add any additional trusted setup here (e.g., cloning repos,
# installing packages, fetching credentials) while sudo is
# still available.
# Drop sudo access for daytona user
sudo rm -f /etc/sudoers.d/daytona
# Mark setup as complete
sudo touch "$GUARD_FILE"
# Hand off to whatever the user passes in
exec "$@"The entrypoint uses sudo to configure iptables, generate the CA, and start iron-proxy as root. Once setup is complete, it removes the daytona user’s sudoers entry so that your workload cannot escalate to root. This is important: the iptables rules allow traffic from root so that iron-proxy itself can reach upstream servers. If your workload could escalate to root via sudo, it would bypass the proxy entirely.
If you do not drop sudo access in the entrypoint, you must do so somewhere in your workload before running untrusted code. Any process that can escalate to root will bypass iron-proxy’s iptables rules.
The guard file (/var/run/iron-proxy-setup-done) ensures the setup logic only runs once. If the entrypoint is invoked again, it skips straight to executing your command.
Create the Dockerfile
Save this as Dockerfile. It uses a multi-stage build to pull the iron-proxy binary and bundle it into a Debian base image. The daytona user is created with passwordless sudo so the entrypoint can perform privileged setup, then the sudoers entry is removed at the end of the entrypoint.
FROM ironsh/iron-proxy:latest AS iron-proxy
FROM debian:trixie-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
iptables iproute2 ca-certificates openssl procps sudo \
&& rm -rf /var/lib/apt/lists/*
COPY --from=iron-proxy /usr/local/bin/iron-proxy /usr/local/bin/iron-proxy
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh
RUN mkdir -p /etc/iron-proxy
COPY iron-proxy.yaml /etc/iron-proxy/config.yaml
RUN useradd -m -s /bin/bash daytona && \
echo "daytona ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/daytona
USER daytona
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]Create the Sandbox
Use the Daytona Python SDK to build the image and launch a sandbox. Save this as create_sandbox.py:
from daytona import Daytona, DaytonaConfig, Image, CreateSandboxFromImageParams
config = DaytonaConfig(api_key="<your-api-key>")
daytona = Daytona(config)
image = Image.from_dockerfile("Dockerfile")
sandbox = daytona.create(
CreateSandboxFromImageParams(image=image),
timeout=0,
on_snapshot_create_logs=print,
)
print(sandbox)Run it:
python create_sandbox.pyDaytona will build the Docker image using its declarative builder , then launch a sandbox from the resulting image. The first build may take longer while the image layers are cached.
Verify
Once the sandbox is running, verify that iron-proxy is intercepting traffic:
curl -sv https://httpbin.org/get 2>&1 | grep "issuer"If iron-proxy is working, the TLS certificate issuer will be iron-proxy CA rather than the real upstream issuer.
You can also check the iron-proxy logs:
cat /var/log/iron-proxy.logCustomizing the Allowlist
Edit the domains array in iron-proxy.yaml to restrict which domains are accessible:
transforms:
- name: allowlist
config:
warn: false
domains:
- "registry.npmjs.org"
- "pypi.org"
- "api.github.com"Set warn to false to block non-allowlisted requests. Rebuild the image and create a new sandbox for changes to take effect.
Trusting the CA
The entrypoint script trusts the CA system-wide via update-ca-certificates. Most tools will work without additional configuration. If you run into TLS errors, see the CA certificate reference for per-runtime details.
Troubleshooting
iron-proxy Is Not Running
Check the log file:
cat /var/log/iron-proxy.logIf the file is empty or missing, the entrypoint script may have failed before starting iron-proxy. Run the entrypoint manually to see errors:
/usr/local/bin/entrypoint.sh bashDNS Resolution Fails
Verify that /etc/resolv.conf points at 127.0.0.1:
cat /etc/resolv.confIf it was overwritten, the entrypoint may not have run correctly. Check that the container is using the custom entrypoint and not a default one.
TLS Certificate Errors
If you see certificate signed by unknown authority, the CA is not trusted by the runtime making the request. Check that update-ca-certificates ran successfully during container startup, and ensure the appropriate environment variable is set for your runtime. See CA Certificates for details.