JCC hardened mode (network isolation)

Restrict outbound network access from the JCC process on a Groundplex node so that Script Snaps and other pipeline code cannot reach unauthorized networks or cloud metadata services.

Overview

Network isolation applies OS-level egress filtering to a Groundplex JCC node, scoped to the snapuser account (UID 1000 by default) that runs pipelines and Snaps. Using iptables with the xt_owner match module, you can firewall outbound connections by process owner rather than by network interface, so rules apply only to traffic generated by the JCC process and not to root-owned system services such as startup scripts or package management. This limits the blast radius of a Script Snap that calls native Java functions to reach an unauthorized network, and it can block access to cloud-provider instance metadata services that expose IAM credentials. This protection applies to connections the JCC process makes directly; it does not by itself restrict destinations reachable through a configured forward proxy (see Limitations).

Network isolation is a separate, customer-managed configuration from JCC hardened mode (file system isolation). File-system isolation hides sensitive host files from the JCC process; network isolation restricts what the JCC process can reach over the network. The two protections are independent and can be enabled together or separately.

Note: Network isolation is configured entirely at the host OS level, using standard Linux firewall tooling. It is not a Snaplex environment variable or a Manager setting, and SnapLogic does not manage or verify these rules on your behalf.

Prerequisites

  • A Groundplex node running on a Linux host with iptables installed.
  • The xt_owner kernel module loaded. Check with lsmod | grep xt_owner, and load it with modprobe xt_owner if it is not present.
  • Root or sudo access on the Groundplex host to configure firewall rules.
  • Root/sudo access to confirm the UID of the account that runs the JCC process. These examples assume the default snapuser account, but every command below uses a $SNAPUSER_UID shell variable rather than a hardcoded UID, set from id -u snapuser (see Configure network isolation), so the rules apply correctly even if your host's snapuser account uses a UID other than the default 1000.
  • An inventory of the network endpoints your pipelines require: DNS, any private network ranges, and the specific hosts or domains your integrations connect to. You will allow-list these explicitly, so pipelines that reach undocumented endpoints will need those endpoints added after the fact (see Allow-list specific endpoints). This inventory must also include the endpoints the JCC itself requires for normal Groundplex operation. Groundplex requirements: Network describes the required ports; Add the SnapLogic Platform to your Allowlist is the maintained source for the actual control-plane and S3 IP addresses to allow-list, since iptables filters by IP address, not hostname. The final deny-all rule in this procedure blocks that traffic too if it is not allow-listed.

Configure network isolation

The xt_owner match filters outbound packets by the UID of the process that created them.

Because iptables evaluates rules in order and stops at the first match, the allow rules for snapuser must come before the final deny-all rule for that user — and no earlier rule in the chain can already accept that traffic.

The examples below assume a clean OUTPUT chain: no pre-existing rules for this UID, and no earlier ACCEPT rule that could match snapuser traffic.

Scoping an earlier rule to an interface, protocol, or destination does not make it safe. Only a rule that itself excludes this UID — for example, with -m owner ! --uid-owner "$SNAPUSER_UID" — or one that provably never carries snapuser traffic can safely precede the rules below. For example, both of these catch-all rules would break the procedure, even though the second is scoped to a single interface:

iptables -A OUTPUT -j ACCEPT
iptables -A OUTPUT -o eth0 -j ACCEPT

Appending rules after either kind of rule has no effect, because iptables stops evaluating once the earlier rule matches, so the chain never reaches the UID-specific rules added here — including the final deny-all rule — and the isolation this procedure describes is silently not enforced.

Run this command first and review every existing rule:

iptables -L OUTPUT -v -n --line-numbers

If your host already has rules for this UID, or a broad accept rule that could match snapuser traffic, resolve that first — remove the conflicting rule, or insert your rules ahead of it with -I — rather than appending on top of an unknown configuration.

Note: These rules apply only to the network namespace in which you add them. If the JCC process runs in a container that does not use host networking, applying these rules to the host's OUTPUT chain does not restrict the container's egress; see Limitations for containerized deployments.

Set a shell variable for the JCC user's UID, so every command below targets the correct account even if your host's snapuser UID differs from the default. Run the remaining commands in this section from the same shell session so the variable stays set:

SNAPUSER_UID=$(id -u snapuser)
  1. Allow loopback traffic for snapuser. This example accepts all loopback traffic, which also accepts connections to any local TCP/UDP service listening on 127.0.0.1 — including a local forward proxy, SSH tunnel, or metadata/credential helper that itself relays traffic to remote networks. Such a service could be used to bypass the endpoint allow-list below entirely:
    iptables -A OUTPUT -m owner --uid-owner "$SNAPUSER_UID" -o lo -j ACCEPT
    If you know the specific local ports the JCC requires — for example, a local health-check or metrics endpoint — restrict this rule to those ports instead of allowing all of lo. If you cannot enumerate every required local port, audit what listens on loopback on this host instead:
    ss -tulnp
    Remove or restrict any local service that could relay traffic externally on snapuser's behalf.
  2. Explicitly block the cloud metadata service endpoints, before adding any other allow rule below. Placing this rule immediately after the loopback rule — ahead of the established/related accept and every endpoint allow rule that follows — means no other allow rule in this chain can accept traffic to these addresses before this rule is reached. It also keeps the block in effect even if the final deny-all rule is later removed or reordered:
    iptables -A OUTPUT -m owner --uid-owner "$SNAPUSER_UID" -d 169.254.169.254 -j DROP
    iptables -A OUTPUT -m owner --uid-owner "$SNAPUSER_UID" -d 169.254.170.2 -j DROP
    Note: Because this rule precedes the established/related accept rule below, it also drops packets on a connection to these addresses that was already established before you added these rules — iptables evaluates this DROP first regardless of connection state. This is the one case in this procedure where an existing connection does not continue; see the next step for how established connections to other destinations are handled.
  3. Allow established and related connections, so responses to requests the JCC already made are not dropped:
    iptables -A OUTPUT -m owner --uid-owner "$SNAPUSER_UID" -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
    Note: This rule also keeps allowing responses on connections the JCC opened before you added these rules, even to destinations you don't allow-list below, until those connections close. Stop the JCC process before beginning this procedure for immediate enforcement, and restart it once Verify that isolation is active confirms the rules are correct.
  4. Allow DNS resolution to only the resolver addresses your host is configured to use. A rule that allows port 53 to any address lets snapuser query an arbitrary external resolver, bypassing the endpoint allow-list below. Find your configured resolvers:
    grep nameserver /etc/resolv.conf
    Then add an allow rule for each resolver address returned (replace 203.0.113.53 with your resolver's address, and repeat for each additional resolver):
    iptables -A OUTPUT -m owner --uid-owner "$SNAPUSER_UID" -d 203.0.113.53 -p udp --dport 53 -j ACCEPT
    iptables -A OUTPUT -m owner --uid-owner "$SNAPUSER_UID" -d 203.0.113.53 -p tcp --dport 53 -j ACCEPT
  5. If your pipelines connect to endpoints on private network ranges, allow only the specific host or subnet each integration actually uses rather than an entire RFC 1918 range. A rule for a full range such as 10.0.0.0/8 grants snapuser access to every address in that range, not just your pipeline's endpoints, which weakens the isolation this procedure is meant to provide:
    iptables -A OUTPUT -m owner --uid-owner "$SNAPUSER_UID" -d 203.0.113.0/24 -j ACCEPT
    Only allow a broad range such as 10.0.0.0/8, 172.16.0.0/12, or 192.168.0.0/16 in its entirety if you specifically intend for snapuser to reach every host on that network, and document that this intentionally bypasses least-privilege filtering.
  6. Allow the endpoints the JCC itself requires for normal Groundplex operation. The final deny-all rule below blocks this traffic, along with pipeline traffic, if it is not allow-listed here — verify these rules before proceeding, or you can strand the Groundplex.

    Control plane and S3: Groundplex requirements: Network lists the required ports (primarily 443), but not the destination IP addresses, and iptables filters by IP address, not hostname. Get the current IP addresses for your SnapLogic environment and region — Production - Global, EMEA, Elastic2, or UAT — from Add the SnapLogic Platform to your Allowlist, the maintained source for these addresses. Add an allow rule for each address listed for your environment:

    iptables -A OUTPUT -m owner --uid-owner "$SNAPUSER_UID" -d 34.208.89.109/32 -p tcp --dport 443 -j ACCEPT

    Repeat for every IP address listed for your environment. Because these addresses can change, re-check that page and update your rules if a previously working control-plane connection starts failing.

    Inter-node and FeedMaster traffic: These are the IP addresses of your own Snaplex nodes, not published SnapLogic addresses. For each peer node, allow the ports Groundplex requirements: Network lists for inter-node communication (8081, 8084, 8089) and health/monitoring (8090, 8091, or your reconfigured port):

    iptables -A OUTPUT -m owner --uid-owner "$SNAPUSER_UID" -d 203.0.113.30 -p tcp --dport 8081 -j ACCEPT

    Repeat for each required port and each peer node in your Snaplex.

  7. Add allow rules for any specific external endpoints your integrations require. See Allow-list specific endpoints for examples.
  8. Add the default deny rule for snapuser last, after every allow rule above:
    iptables -A OUTPUT -m owner --uid-owner "$SNAPUSER_UID" -j DROP
Important: For this initial setup, add rules in the order shown, using -A (append) throughout. Because -A adds each rule to the end of the chain, running these commands in order produces a chain that is evaluated correctly: specific allow rules first, deny-all last. Inserting rules out of order at this stage, or using -I (insert) without accounting for the chain you're building, can silently reorder the deny-all rule ahead of an allow rule and block traffic you intended to permit. Once the deny-all rule is in place, adding further endpoints requires -I instead — see Allow-list specific endpoints.

Allow-list specific endpoints

Add an explicit allow rule for each additional endpoint your pipelines require.

By the time you use this section, the deny-all rule for snapuser is already the last rule in the chain, so appending a new rule with -A would add it after the deny-all rule, where it can never match. Instead, find the deny-all rule's current line number and insert (-I) the new allow rule immediately above it:

iptables -L OUTPUT --line-numbers -n | grep "owner UID match $SNAPUSER_UID" | grep DROP
# 10        0     0 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0            owner UID match 1000

Use the line number from that output (10 in this example) as the insert position below. Re-run this check before each new insertion — the deny-all rule's line number increases each time you insert a rule above it.

Also check for any earlier ACCEPT rule above that insert position that could match snapuser traffic. Scoping alone to an interface, protocol, or destination does not exclude this UID — only a rule that itself excludes it is safe to leave in place. If an unsafe rule exists, both the new allow rule and the deny-all rule below it are silently skipped for traffic that already matched the earlier accept.

Because iptables matches on IP address rather than hostname, resolve the hostname first and allow-list the resulting address:

A specific HTTPS endpoint

getent hosts api.example.com
# 203.0.113.10 api.example.com

iptables -I OUTPUT 10 -m owner --uid-owner "$SNAPUSER_UID" -d 203.0.113.10 -p tcp --dport 443 -j ACCEPT

A database host

iptables -I OUTPUT 10 -m owner --uid-owner "$SNAPUSER_UID" -d 203.0.113.20 -p tcp --dport 5432 -j ACCEPT
Attention: Some SaaS and CDN-fronted endpoints resolve to different IP addresses over time or serve traffic from a range of addresses. An allow rule for a single resolved IP address can stop working if the endpoint's address changes. If a previously working connection starts failing, re-resolve the hostname and confirm the allow rule still matches the current address, or allow-list the provider's published IP range if one is available.

Verify that isolation is active

List the OUTPUT chain with packet and byte counters to confirm the rules are in place and matching traffic:

iptables -L OUTPUT -v -n --line-numbers

Test from a shell running as snapuser to confirm allowed and blocked destinations behave as expected:

sudo -u snapuser curl -m 5 -I https://api.example.com
sudo -u snapuser curl -m 2 --noproxy '*' -X PUT \
  -H "X-aws-ec2-metadata-token-ttl-seconds: 21600" \
  http://169.254.169.254/latest/api/token

The first command should succeed for an allow-listed endpoint. For the second command, a connection timeout, Connection refused, or No route to host from curl — that is, no HTTP status code at all — confirms the metadata service is unreachable. Do not judge this by the HTTP status code alone: when AWS enforces IMDSv2, the metadata service returns 401 for a request that omits a valid token, so a service that is still reachable can return an error status rather than the requested data. Any HTTP response, including an error status, means the destination was reached and the block is not in effect. For a protocol-independent check, use a raw TCP probe instead: sudo -u snapuser nc -w 2 -z 169.254.169.254 80 && echo reachable || echo blocked.

Limitations

  • Requires the xt_owner kernel module. If the module is not available on your host or distribution, UID-based filtering cannot be applied.
  • Rule order is critical. Allow rules for snapuser must precede the final deny-all rule, or traffic you intended to permit is dropped.
  • ICMP responses do not always carry the originating socket's UID. A rule that blocks all ICMP traffic for snapuser can drop responses needed for path MTU discovery and similar diagnostics. Avoid blanket ICMP deny rules scoped to this UID.
  • These rules use iptables, which filters IPv4 traffic only. If the host has IPv6 enabled and can reach the network over IPv6, the JCC process can bypass this filtering entirely unless you apply equivalent ip6tables rules using the same xt_owner match, or disable IPv6 on the host.
  • If the Snaplex is configured to use an HTTP/S forward proxy (see Groundplex Network Setup), these rules only see the JCC's connection to the proxy. The proxy can then connect to any destination on the JCC's behalf, so pipeline code routed through the proxy can reach a destination that isn't in this page's allow-list. Apply equivalent destination filtering on the proxy itself, or these rules do not fully prevent pipeline code from reaching unauthorized networks.
  • These iptables rules are not persistent across a host reboot unless you separately configure a persistence mechanism for your distribution (for example, iptables-persistent on Debian-based systems, or saving rules with your distribution's equivalent). Confirm your rules are still active after any host restart.
  • Network isolation restricts what the JCC process can reach; it does not restrict what files the JCC process can read or write. For file-system isolation, see JCC hardened mode (file system isolation).
  • A host administrator applies these rules to the host's network stack; the JCC process itself does not need any special capability to be filtered by them. This works when the JCC process runs directly on the Linux host, or in a container that uses host networking and therefore shares the host's network namespace and OUTPUT chain. Do not grant the JCC container the NET_ADMIN capability to try to satisfy this requirement — a JCC process with NET_ADMIN could modify or flush these firewall rules itself, defeating the isolation. A container that does not use host networking needs equivalent restrictions applied at the host or orchestration layer instead; see the note about network namespaces in Configure network isolation.

Troubleshooting

A pipeline fails to reach an endpoint after applying rules

Symptom: A pipeline or Snap that worked before the rules were applied now times out or fails to connect.

Fix: Run iptables -L OUTPUT -v -n --line-numbers and check whether packet counters are increasing on the deny-all rule for snapuser's UID. If so, identify the destination address and port the pipeline needs and add an allow rule for it before the deny-all rule (see Allow-list specific endpoints).

Rules appear to have no effect

Symptom: Traffic that should be blocked is still reaching its destination.

Fix: Confirm the xt_owner module is loaded (lsmod | grep xt_owner) and that the JCC process is actually running as the UID the rules target (ps -o pid,uid,cmd -C jcc or equivalent). Rules written against the wrong UID silently match nothing.

Roll back network isolation

Remove the rules you added for snapuser. List the chain with line numbers (iptables -L OUTPUT -v -n --line-numbers) and delete each rule by number, starting from the highest number so earlier line numbers don't shift:

iptables -D OUTPUT <line-number>

Re-run the verification steps to confirm the rules are removed and connectivity is restored.