Skip to content

Conversation

@clemlesne
Copy link

@clemlesne clemlesne commented Jan 20, 2026

Summary

Add new configuration option blockAllOutbound that blocks all guest-initiated outbound TCP/UDP connections at the network layer, while preserving host-to-guest port forwarding.

Problem

DNS whitelist filtering (#599) blocks domain resolution for unauthorized domains, but guests can still connect to hardcoded IP addresses (e.g., curl http://93.184.216.34). This is a security gap for sandboxed environments that require true network isolation.

Solution

When blockAllOutbound: true, the TCP and UDP forwarders reject all guest-initiated outbound connections before they reach net.Dial(). This provides defense-in-depth alongside DNS filtering.

blockAllOutbound: true
forwards:
  "127.0.0.1:8080": "192.168.127.2:8080"  # Host→Guest still works

Use Cases

  1. Sandboxed code execution - Run untrusted code with port forwarding for health checks, but no ability to exfiltrate data via direct IP connections
  2. Isolated testing environments - Expose services for testing while preventing outbound traffic
  3. Air-gapped workloads - Complete network isolation except for explicit port forwards

What Still Works vs What's Blocked

Feature blockAllOutbound: false blockAllOutbound: true
Guest → Internet (TCP) ✅ Allowed ❌ Blocked
Guest → Internet (UDP) ✅ Allowed ❌ Blocked
Guest → Direct IP ✅ Allowed ❌ Blocked
Host → Guest (Forwards) ✅ Works ✅ Works
DHCP (IP assignment) ✅ Works ✅ Works
DNS (via zones) ✅ Works ✅ Works

Combined with DNS Filtering (#599)

For maximum isolation, combine both features:

blockAllOutbound: true          # Block all outbound TCP/UDP
zones:
  - name: "."
    defaultIP: "0.0.0.0"        # Block DNS for unlisted domains
    records:
      - regexp: "^(.*\\.)?allowed\\.example\\.com\\.?$"
        # Whitelist (forwards to upstream)
forwards:
  "127.0.0.1:8080": "192.168.127.2:8080"  # Only way to reach guest

This ensures:

  • Guest cannot resolve unauthorized domains (DNS blocked)
  • Guest cannot connect to any IP directly (TCP/UDP blocked)
  • Host can still reach guest via explicit port forwards

Implementation

  • pkg/types/configuration.go - Add BlockAllOutbound bool field
  • pkg/services/forwarder/tcp.go - Early return when flag is true
  • pkg/services/forwarder/udp.go - Early return when flag is true
  • pkg/virtualnetwork/services.go - Pass flag to forwarders

Test Plan

  • Verify guest cannot initiate outbound TCP connections
  • Verify guest cannot initiate outbound UDP connections
  • Verify host→guest port forwarding still works via Forwards config
  • Verify DHCP still works (guest gets IP)
  • Verify DNS still works (filtered by zones)

🤖 Generated with Claude Code

Add a new configuration option `blockAllOutbound` that blocks all
guest-initiated outbound TCP/UDP connections while still allowing:
- Host to guest port forwarding (PortsForwarder)
- DHCP (guest can still get IP)
- DNS (handled separately by gateway)

This is useful for network isolation scenarios where the guest should
only be reachable via explicit port forwards from the host.
@openshift-ci
Copy link
Contributor

openshift-ci bot commented Jan 20, 2026

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: clemlesne
Once this PR has been reviewed and has the lgtm label, please assign lstocchi for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

translation := parseNATTable(configuration)

tcpForwarder := forwarder.TCP(s, translation, &natLock, configuration.Ec2MetadataAccess)
tcpForwarder := forwarder.TCP(s, translation, &natLock, configuration.Ec2MetadataAccess, configuration.BlockAllOutbound)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you tried to not create the forwarders when configuration.BlockAllOutbound is set, instead of doing it inside the forwarder code?

if !configuration.BlockAllOutbound {
	tcpForwarder := forwarder.TCP(s, translation, &natLock, configuration.Ec2MetadataAccess)
	s.SetTransportProtocolHandler(tcp.ProtocolNumber, tcpForwarder.HandlePacket)
	udpForwarder := forwarder.UDP(s, translation, &natLock)
	s.SetTransportProtocolHandler(udp.ProtocolNumber, udpForwarder.HandlePacket)
}

@cfergeau
Copy link
Collaborator

cfergeau commented Feb 4, 2026

We’ll also need a DCO in the commit log for your PRs to be mergeable:

There is one commit incorrectly signed off. This means that the author of this commit failed to include a Signed-off-by line in the commit message.

To avoid having PRs blocked in the future, always include Signed-off-by: Author Name authoremail@example.com in every commit message. You can also do this automatically by using the -s flag (i.e., git commit -s).

@cfergeau
Copy link
Collaborator

cfergeau commented Feb 6, 2026

DNS whitelist filtering (#599) blocks domain resolution for unauthorized domains, but guests can still connect to hardcoded IP addresses (e.g., curl http://93.184.216.34). This is a security gap for sandboxed environments that require true network isolation.

#599 says:

When running untrusted code in sandboxed VMs, you often need to:

  • Allow access to specific domains (e.g., package registries like pypi.org)
  • Block all other domains by default

#599 is imperfect as using IPs is not blocked. However, this PR will block everything, there is no way to whitelist certain domains as in #599. Would have been nice to be able to get the best of both worlds…

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants