Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@ The main command groups are:
- `osb command`: command execution and persistent sessions
- `osb file`: file and directory operations
- `osb egress`: runtime egress policy
- `osb credential-vault`: manage the Credential Vault
- `osb diagnostics`: stable diagnostics logs and events
- `osb devops`: experimental legacy diagnostics
- `osb config`: local CLI configuration
Expand All @@ -327,6 +328,7 @@ Bundled skills:
- `command-execution`
- `file-operations`
- `network-egress`
- `credential-vault`
- `sandbox-troubleshooting`

Supported targets:
Expand Down
8 changes: 7 additions & 1 deletion cli/src/opensandbox_cli/commands/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,13 @@ def _handle_execution_error(obj: ClientContext, execution) -> None:
@click.option("-d", "--background", is_flag=True, default=False, help="Run in background.")
@click.option("-w", "--workdir", default=None, help="Working directory.")
@click.option("-t", "--timeout", type=DURATION, default=None, help="Command timeout (e.g. 30s, 5m).")
@output_option("table", "json", "yaml", "raw")
@output_option(
"table",
"json",
"yaml",
"raw",
help_text="Output format. Foreground mode: raw only (default). Background mode: table, json, or yaml.",
)
@click.pass_obj
@handle_errors
def command_run(
Expand Down
3 changes: 0 additions & 3 deletions cli/src/opensandbox_cli/commands/sandbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,6 @@ def sandbox_group(ctx: click.Context) -> None:
click.echo(ctx.get_help())


# Alias: osb sb ...
sandbox_group.name = "sandbox"

_SANDBOX_STATE_CANONICAL = {
state.lower(): state for state in SandboxState.values()
}
Expand Down
4 changes: 1 addition & 3 deletions cli/src/opensandbox_cli/commands/skills.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ class TargetConfig(TypedDict):
"command-execution": "Execution",
"file-operations": "Files",
"network-egress": "Network",
"credential-vault": "Credentials",
"sandbox-troubleshooting": "Troubleshooting",
}

Expand Down Expand Up @@ -418,9 +419,6 @@ def _install_guidance_text() -> str:
" osb skills install <skill-name> --target <tool> --scope <scope>\n\n"
" Install all bundled skills for one tool:\n"
" osb skills install --all-builtins --target <tool> --scope <scope>\n\n"
" Discover skills and targets:\n"
" osb skills list\n"
" osb skills show <skill-name>\n\n"
f" Available skills: {', '.join(_ALL_SKILL_NAMES)}\n"
f" Available targets: {', '.join(_ALL_TARGET_NAMES)}\n"
f" Available scopes: {', '.join(_ALL_SCOPE_NAMES)}"
Expand Down
2 changes: 1 addition & 1 deletion cli/src/opensandbox_cli/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ def select_output_format(
allowed: tuple[str, ...],
fallback: str,
) -> str:
"""Resolve a command-scoped output format from explicit input, config, and fallback."""
"""Resolve a command-scoped output format from explicit input and fallback."""
if requested:
if requested not in allowed:
allowed_list = ", ".join(allowed)
Expand Down
2 changes: 1 addition & 1 deletion cli/tests/test_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def _make_output(fmt: str) -> OutputFormatter:
ctx.make_output.side_effect = _make_output
ctx.get_manager.return_value = manager or MagicMock()
ctx.connect_sandbox.return_value = sandbox or MagicMock()
ctx.resolve_sandbox_id.side_effect = lambda prefix: prefix # passthrough
ctx.resolve_sandbox_id.side_effect = lambda prefix: prefix
ctx.connection_config = MagicMock()
ctx.close = MagicMock()
return ctx
Expand Down
11 changes: 7 additions & 4 deletions components/egress/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,13 @@ RUN chown mitmproxy:mitmproxy /var/lib/mitmproxy/.mitmproxy/config.yaml \
COPY --from=builder /out/egress /opt/opensandbox-egress/egress
COPY --from=builder /out/opensandbox-supervisor /opt/opensandbox-egress/supervisor
# Pre-start hook: reap any mitmdump left over from a previous crashed
# egress so the new launch can bind the transparent-MITM listen port.
# Intentionally does NOT touch iptables/nft rules — the sidecar shares
# a network namespace with the workload, so leaving rules in place keeps
# egress filtering active across the supervisor's backoff window.
# egress so the new launch can bind the transparent-MITM listen port, and
# remove stale redirect rules (iptables DNS 53 redirect, transparent-MITM
# REDIRECT, and the opensandbox_dns_redirect nft table) left pointing at the
# dead processes. It intentionally leaves the `inet opensandbox` policy
# table untouched — the sidecar shares a network namespace with the
# workload, so leaving the filter rules in place keeps egress enforcement
# active across the supervisor's backoff window.
COPY components/egress/scripts/cleanup.sh /opt/opensandbox-egress/cleanup.sh
RUN chmod 0755 /opt/opensandbox-egress/cleanup.sh \
/opt/opensandbox-egress/egress \
Expand Down
7 changes: 3 additions & 4 deletions components/egress/docs/opentelemetry.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Do not drop them: the instrument records **seconds**, while the SDK default boun
the spec's millisecond ladder (`0, 5, 10, … 10000`), so every realistic latency would fall
into the single `le=5` bucket and the quantiles would be meaningless.

The head resolves a cache hit (sub-millisecond) up to one upstream timeout
The head spans a fast single-upstream exchange (sub-millisecond) up to one upstream timeout
(`OPENSANDBOX_EGRESS_DNS_UPSTREAM_TIMEOUT`, 5s by default). The coarse tail exists because
the recorded duration covers the **whole resolver chain**: forwarding walks the upstreams
serially, each with the full timeout, so a query can legitimately take
Expand Down Expand Up @@ -84,12 +84,11 @@ All egress metrics may include shared attributes:

## OTEL Endpoint Configuration

Metric export is enabled only when at least one OTLP endpoint is set.
Metric export is enabled when an OTLP endpoint is set, or when a node IP is available via `HOST_IP` or `/etc/hostinfo` (metrics are then exported insecurely to `<ip>:4318`).

- `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT` (preferred)
- `OTEL_EXPORTER_OTLP_ENDPOINT` (fallback)

If both are unset, egress keeps metrics local (no OTLP export).
- `HOST_IP` (fallback endpoint host when both OTLP env vars are unset)

### Minimal Example

Expand Down
1 change: 0 additions & 1 deletion components/egress/nameserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ func TestAllowIPsForNft_EmptyResolv(t *testing.T) {
func TestAllowIPsForNft_ValidNameservers(t *testing.T) {
dir := t.TempDir()
resolv := filepath.Join(dir, "resolv.conf")
// Standard resolv.conf with two nameservers
content := "nameserver 192.168.65.7\nnameserver 10.0.0.1\n"
require.NoError(t, os.WriteFile(resolv, []byte(content), 0644))
ips := AllowIPsForNft(resolv)
Expand Down
1 change: 0 additions & 1 deletion components/egress/pkg/policy/policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ func TestWithExtraAllowIPs(t *testing.T) {
require.Len(t, allowV6, 1, "allowV6 length mismatch")
require.Equal(t, "2001:db8::1", allowV6[0])

// nil/empty ips returns same policy
require.Same(t, p, p.WithExtraAllowIPs(nil), "WithExtraAllowIPs(nil) should return same policy")
require.Same(t, p, p.WithExtraAllowIPs([]netip.Addr{}), "WithExtraAllowIPs([]) should return same policy")
}
Expand Down
2 changes: 1 addition & 1 deletion components/egress/pkg/telemetry/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ func registerEgressMetrics() error {
// boundaries are the spec's millisecond ladder (0, 5, 10, ... 10000), so every
// realistic DNS latency lands in the same bucket and the quantiles are noise.
//
// The head spans a cache hit (sub-ms) to one upstream timeout
// The head spans a fast single-upstream exchange (sub-ms) to one upstream timeout
// (DefaultDNSUpstreamTimeoutSec = 5s). The coarse tail covers the retry chain:
// forward() walks the resolvers serially, each with the full timeout, and the
// recorded duration is the whole chain — so a query can legitimately take
Expand Down
3 changes: 2 additions & 1 deletion components/egress/policy_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ type nftApplier interface {
RemoveEnforcement(context.Context) error
}

// startPolicyServer: runtime POST/GET /policy, GET /healthz. nameserverIPs are merged into every nft
// startPolicyServer serves the runtime policy API (GET/POST/PUT/PATCH/DELETE /policy,
// /credential-vault* subroutes) and GET /healthz. nameserverIPs are merged into every nft
// static apply so the pod’s resolv / private DNS still works alongside user egress rules.
func startPolicyServer(
proxy policyUpdater,
Expand Down
3 changes: 2 additions & 1 deletion components/egress/policy_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@ func removeRulesByTarget(rules []policy.EgressRule, targets []string) (kept, rem
return kept, removed
}

// mergeKey: domain targets lowercased for dedupe; IP/CIDR left as-is.
// mergeKey lowercases targets for dedupe (IP/CIDR strings are lowered too,
// which is lossless for canonical IP/CIDR forms).
func mergeKey(r policy.EgressRule) string {
if r.Target == "" {
return r.Target
Expand Down
32 changes: 16 additions & 16 deletions components/execd/RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,8 @@ Thanks to these contributors ❤️
- @csdbianhua

---
- Docker Hub: opensandbox/execd:v1.0.9
- Aliyun Registry: sandbox-registry.cn-zhangjiakou.cr.aliyuncs.com/opensandbox/execd:v1.0.9
- Docker Hub: opensandbox/execd:v1.0.8
- Aliyun Registry: sandbox-registry.cn-zhangjiakou.cr.aliyuncs.com/opensandbox/execd:v1.0.8

# components/execd 1.0.7

Expand Down Expand Up @@ -144,8 +144,8 @@ Thanks to these contributors ❤️
- @dependabot

---
- Docker Hub: opensandbox/execd:v1.0.9
- Aliyun Registry: sandbox-registry.cn-zhangjiakou.cr.aliyuncs.com/opensandbox/execd:v1.0.9
- Docker Hub: opensandbox/execd:v1.0.7
- Aliyun Registry: sandbox-registry.cn-zhangjiakou.cr.aliyuncs.com/opensandbox/execd:v1.0.7

# components/execd 1.0.6

Expand All @@ -167,8 +167,8 @@ Thanks to these contributors ❤️
- @dependabot

---
- Docker Hub: opensandbox/execd:v1.0.9
- Aliyun Registry: sandbox-registry.cn-zhangjiakou.cr.aliyuncs.com/opensandbox/execd:v1.0.9
- Docker Hub: opensandbox/execd:v1.0.6
- Aliyun Registry: sandbox-registry.cn-zhangjiakou.cr.aliyuncs.com/opensandbox/execd:v1.0.6

# components/execd 1.0.5

Expand All @@ -185,8 +185,8 @@ Thanks to these contributors ❤️
- @Pangjiping

---
- Docker Hub: opensandbox/execd:v1.0.9
- Aliyun Registry: sandbox-registry.cn-zhangjiakou.cr.aliyuncs.com/opensandbox/execd:v1.0.9
- Docker Hub: opensandbox/execd:v1.0.5
- Aliyun Registry: sandbox-registry.cn-zhangjiakou.cr.aliyuncs.com/opensandbox/execd:v1.0.5

# components/execd 1.0.4

Expand All @@ -209,8 +209,8 @@ Thanks to these contributors ❤️
- @ninan-nn

---
- Docker Hub: opensandbox/execd:v1.0.9
- Aliyun Registry: sandbox-registry.cn-zhangjiakou.cr.aliyuncs.com/opensandbox/execd:v1.0.9
- Docker Hub: opensandbox/execd:v1.0.4
- Aliyun Registry: sandbox-registry.cn-zhangjiakou.cr.aliyuncs.com/opensandbox/execd:v1.0.4

# components/execd 1.0.3

Expand All @@ -233,8 +233,8 @@ Thanks to these contributors ❤️
- @jwx0925

---
- Docker Hub: opensandbox/execd:v1.0.9
- Aliyun Registry: sandbox-registry.cn-zhangjiakou.cr.aliyuncs.com/opensandbox/execd:v1.0.9
- Docker Hub: opensandbox/execd:v1.0.3
- Aliyun Registry: sandbox-registry.cn-zhangjiakou.cr.aliyuncs.com/opensandbox/execd:v1.0.3

# components/execd 1.0.2

Expand Down Expand Up @@ -262,8 +262,8 @@ Thanks to these contributors ❤️
- @ninan-nn

---
- Docker Hub: opensandbox/execd:v1.0.9
- Aliyun Registry: sandbox-registry.cn-zhangjiakou.cr.aliyuncs.com/opensandbox/execd:v1.0.9
- Docker Hub: opensandbox/execd:v1.0.2
- Aliyun Registry: sandbox-registry.cn-zhangjiakou.cr.aliyuncs.com/opensandbox/execd:v1.0.2

# components/execd 1.0.1

Expand Down Expand Up @@ -295,8 +295,8 @@ Thanks to these contributors ❤️
- @jwx0925

---
- Docker Hub: opensandbox/execd:v1.0.9
- Aliyun Registry: sandbox-registry.cn-zhangjiakou.cr.aliyuncs.com/opensandbox/execd:v1.0.9
- Docker Hub: opensandbox/execd:v1.0.1
- Aliyun Registry: sandbox-registry.cn-zhangjiakou.cr.aliyuncs.com/opensandbox/execd:v1.0.1

# components/execd 1.0.0

Expand Down
Loading
Loading