-
Notifications
You must be signed in to change notification settings - Fork 283
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Create loopback iptables rules for localhost
When a container uses the host network driver (--network=host), it shares the host's network namespace. In this case, it's important to consider the IP address the container's process is bound to. For example: `nerdctl run --network=host -d python:slim python -m http.server 8020 --bind 127.0.0.1` vs `nerdctl run --network=host -d python:slim python -m http.server 8020` If the process is bound to 127.0.0.1 (localhost), we need to create additional iptables rules to allow access to the container’s bound port from outside the network namespace (e.g., localhost:8020). This change ensures that the appropriate iptables rules are created and removed when a container's port is bound to localhost, enabling external access to the bound port. Signed-off-by: Nino Kodabande <[email protected]>
- Loading branch information
Showing
1 changed file
with
61 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,6 +20,7 @@ import ( | |
"encoding/hex" | ||
"fmt" | ||
"net" | ||
"os/exec" | ||
"strconv" | ||
"time" | ||
|
||
|
@@ -29,6 +30,13 @@ import ( | |
"github.com/rancher-sandbox/rancher-desktop/src/go/guestagent/pkg/tracker" | ||
) | ||
|
||
type action string | ||
|
||
const ( | ||
Append action = "append" | ||
Delete action = "delete" | ||
) | ||
|
||
func ForwardPorts(ctx context.Context, tracker tracker.Tracker, scanInterval time.Duration) error { | ||
ticker := time.NewTicker(scanInterval) | ||
defer ticker.Stop() | ||
|
@@ -63,6 +71,10 @@ func ForwardPorts(ctx context.Context, tracker tracker.Tracker, scanInterval tim | |
}) | ||
if err != nil { | ||
log.Errorf("procnet scanner failed to add port: %s", err) | ||
continue | ||
} | ||
if err = execLoopbackIPtablesRule(bindings, port, Append); err != nil { | ||
log.Errorf("procnet scanner creating loopback iptable rules for portbinding: %v failed: %s", bindings, err) | ||
Check failure Code scanning / check-spelling Unrecognized Spelling Error
portbinding is not a recognized word. (unrecognized-spelling)
|
||
} | ||
} | ||
} | ||
|
@@ -74,6 +86,11 @@ func ForwardPorts(ctx context.Context, tracker tracker.Tracker, scanInterval tim | |
err := tracker.Remove(generateID(fmt.Sprintf("%s/%s", port.Proto(), port.Port()))) | ||
if err != nil { | ||
log.Errorf("procnet scanner failed to remove port: %s", err) | ||
continue | ||
} | ||
|
||
if err = execLoopbackIPtablesRule(previousBindings, port, Delete); err != nil { | ||
log.Errorf("procnet scanner deleting loopback iptable rules for portbinding: %v failed: %s", previousBindings, err) | ||
Check failure Code scanning / check-spelling Unrecognized Spelling Error
portbinding is not a recognized word. (unrecognized-spelling)
|
||
} | ||
} | ||
} | ||
|
@@ -83,6 +100,50 @@ func ForwardPorts(ctx context.Context, tracker tracker.Tracker, scanInterval tim | |
} | ||
} | ||
|
||
// execLoopbackIPtablesRule modifies iptables NAT rules to handle loopback traffic for a specified port | ||
// and protocol. This function is only necessary when the container is using the host network driver | ||
// (i.e., with --network=host), as in this case the container shares the host's network namespace. | ||
// | ||
// When using the host network driver, the network traffic that is bound to 127.0.0.1 would need to be | ||
// redirected from outside of the network namespace to the localhost (127.0.0.1). This function adds or | ||
// removes DNAT rules that allow traffic to be forwarded to the specified port on localhost, based on | ||
// the provided 'action' ('append' or 'delete'). | ||
// | ||
// The function iterates over the provided list of port bindings and, for each binding where the HostIP | ||
// is set to 127.0.0.1, it constructs and executes the corresponding iptables command to either add or | ||
// delete the appropriate DNAT rule. | ||
// | ||
// The iptables rule ensures that incoming traffic from outside of the network namespace (i.e., | ||
// from the host windows machine) on the specified port and protocol is redirected to the same | ||
// port on localhost, where the container's service can be accessed. | ||
// | ||
// Example iptables rule when 'action' is "append": | ||
// iptables -t nat -A PREROUTING -p tcp --dport 8009 -j DNAT --to-destination 127.0.0.1:8009 | ||
// | ||
// Example iptables rule when 'action' is "delete": | ||
// iptables -t nat -D PREROUTING -p tcp --dport 8009 -j DNAT --to-destination 127.0.0.1:8009 | ||
|
||
func execLoopbackIPtablesRule(bindings []nat.PortBinding, portProto nat.Port, action action) error { | ||
for _, binding := range bindings { | ||
if binding.HostIP == "127.0.0.1" { | ||
// iptables -t nat -D PREROUTING -p tcp --dport 8009 -j DNAT --to-destination 127.0.0.1:8009 | ||
iptablesCmd := exec.Command("iptables", | ||
"--table", "nat", | ||
fmt.Sprintf("--%s", action), "PREROUTING", | ||
"--protocol", portProto.Proto(), | ||
"--dport", binding.HostPort, | ||
"--jump", "DNAT", | ||
"--to-destination", fmt.Sprintf("%s:%s", binding.HostIP, binding.HostPort), | ||
) | ||
if err := iptablesCmd.Run(); err != nil { | ||
return err | ||
} | ||
log.Debugf("running the following iptables rule [%s] for port bindings: %v", iptablesCmd.String(), binding) | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func addValidProtoEntryToPortMap(entry procnettcp.Entry, portMap nat.PortMap) error { | ||
switch entry.Kind { | ||
case procnettcp.TCP: | ||
|