# Network Security

Sydekyks user and operator guide. Check feature availability and release-specific installation requirements.

## ​Overview

Sydekyks makes outbound HTTP from two surfaces, and each is hardened separately:

- User code in flows: Code steps, piece actions, anything a flow author can write. Hardened by AP_NETWORK_MODE; see User-code egress.

- Server-side HTTP from the API: OAuth token claim and refresh, Vault, Conjur, event-destination webhooks, on-call pager, MCP tool validation. Always filtered, tuned via AP_SSRF_ALLOW_LIST; see Server-side egress.

Both surfaces block the same set of IPs (RFC1918 private, loopback, link-local, cloud-metadata, non-unicast) and share the AP_SSRF_ALLOW_LIST allow-list.

## ​User-code egress

Every flow eventually runs user-supplied code: Code steps, piece actions, HTTP requests. Without an explicit boundary, that code can reach anything the host can reach: 127.0.0.1, Redis, Postgres, the Kubernetes API, cloud-metadata endpoints (169.254.169.254), the VPC. AP_NETWORK_MODE is the switch that controls this boundary.

AP_NETWORK_MODE defaults to UNRESTRICTED. Set it to STRICT to install the in-process SSRF guard described below.

 | Value | Effect

 | UNRESTRICTED | No outbound guard. User code can reach any host the worker can reach.

 | STRICT | The engine SSRF guard is installed. Outbound connections to private, loopback, link-local, and cloud-metadata IPs are blocked from user code.

Related env var:

- AP_SSRF_ALLOW_LIST: comma-separated IPs/CIDRs that bypass the block (for example an internal DB or sidecar). Shared with server-side egress.

## ​How Isolation Works

In STRICT mode the engine installs an in-process SSRF guard before any user code runs. It monkey-patches Node’s dns.lookup and Socket.prototype.connect:

- DNS resolution: when user code resolves a hostname, the guard resolves every A/AAAA record and rejects the lookup if any of them falls in a blocked range. This closes the multi-record bypass where one IP is public and another is private.

- Raw socket connect: when user code opens a socket directly to an IP, the guard checks that IP against the same blocklist.

Together these cover axios, fetch, undici, and raw http/net in a single pass. A blocked target throws SSRFBlockedError. The blocklist is every non-unicast range (RFC1918, loopback, link-local, multicast, cloud metadata), minus anything in AP_SSRF_ALLOW_LIST.

The engine SSRF guard is best-effort, in-process protection. It reliably stops accidental SSRF, such as a flow or piece that naively follows a user-supplied URL to an internal host. It is not a hard boundary against deliberately malicious code: worker_threads, child_process, native addons, and process.binding can all sidestep the JavaScript-level monkey-patches.

For multi-tenant deployments that run untrusted code, enforce the real egress boundary in infrastructure: a VPC firewall, network policy, or egress gateway that blocks the cloud-metadata IP (169.254.169.254) and your private ranges independently of the application. Treat AP_NETWORK_MODE=STRICT as defense-in-depth on top of that, not as a replacement for it.

The guard is independent of the sandbox execution mode. It is installed in every mode when AP_NETWORK_MODE=STRICT. See Sandboxing (https://docs.sydekyks.com/install/architecture/sandboxing) for what each sandbox mode isolates at the process level. Network security is independent: it constrains what code is allowed to reach, regardless of how it runs.

## ​Verifying Your Setup

From a Code step in a test flow, try:

```
const res = await fetch('http://169.254.169.254/latest/meta-data/')

```

With AP_NETWORK_MODE=STRICT you should see an SSRFBlockedError. With UNRESTRICTED the request succeeds if the host allows it, confirming the guard is off.

## ​Rolling Out

- Start in UNRESTRICTED (default) and identify any internal services that legitimate flows need to reach (internal APIs, databases used by Code steps, and so on).

- Add those IPs to AP_SSRF_ALLOW_LIST.

- Switch to AP_NETWORK_MODE=STRICT. Watch worker logs for SSRFBlockedError. Each one is either an attack, a misconfigured flow, or a missing allow-list entry.

## ​Server-side egress

Separate from flow code, the API server itself makes outbound HTTP on behalf of admins and users: OAuth token claim and refresh, Hashicorp Vault, CyberArk Conjur, event-destination webhooks, on-call pager, MCP tool validation. The URLs come from admin config (Vault server URL) or user input (webhook destination, MCP server URL), so the same SSRF risks apply.

Unlike user-code egress, this layer is always on. It does not require AP_NETWORK_MODE=STRICT. It is implemented as a request-filtering-agent wrapper attached to the shared axios instances in @activepieces/server-utils, and every outbound request flows through it. The blocked ranges are identical to the engine guard (RFC1918, loopback, link-local, cloud metadata, non-unicast).

AP_SSRF_ALLOW_LIST is shared between both surfaces. Add an IP or CIDR once and it applies to user-code egress and server-side HTTP. Restart the server after changing the value.

When a request is blocked, the axios error surfaced in the admin UI includes the AP_SSRF_ALLOW_LIST hint, so operators see the fix directly in connection-test dialogs.

### ​Self-hosted providers on private IPs

If Vault, Conjur, an on-prem OAuth2 token endpoint, or an internal webhook resolves to a private IP, the server-side filter rejects it until you add the target to AP_SSRF_ALLOW_LIST:

```
AP_SSRF_ALLOW_LIST=10.0.5.12,192.168.10.0/24

```

### ​Relaxed TLS is still filtered

Connectors that accept self-signed certs (for example CyberArk Conjur in a private cluster) use rejectUnauthorized: false. The SSRF filter still applies under this setting: TLS verification is relaxed, SSRF protection is not.
