mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-27 03:55:36 +00:00
80 lines
2.7 KiB
Go
80 lines
2.7 KiB
Go
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||
|
|
||
|
//go:build linux
|
||
|
|
||
|
package linuxfw
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"net/netip"
|
||
|
)
|
||
|
|
||
|
// This file contains functionality to insert portmapping rules for a 'service'.
|
||
|
// These are currently only used by the Kubernetes operator proxies.
|
||
|
// An iptables rule for such a service contains a comment with the service name.
|
||
|
|
||
|
// EnsurePortMapRuleForSvc adds a prerouting rule that forwards traffic received
|
||
|
// on match port and NOT on the provided interface to target IP and target port.
|
||
|
// Rule will only be added if it does not already exists.
|
||
|
func (i *iptablesRunner) EnsurePortMapRuleForSvc(svc, tun string, targetIP netip.Addr, pm PortMap) error {
|
||
|
table := i.getIPTByAddr(targetIP)
|
||
|
args := argsForPortMapRule(svc, tun, targetIP, pm)
|
||
|
exists, err := table.Exists("nat", "PREROUTING", args...)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("error checking if rule exists: %w", err)
|
||
|
}
|
||
|
if !exists {
|
||
|
return table.Append("nat", "PREROUTING", args...)
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// DeleteMapRuleForSvc constructs a prerouting rule as would be created by
|
||
|
// EnsurePortMapRuleForSvc with the provided args and, if such a rule exists,
|
||
|
// deletes it.
|
||
|
func (i *iptablesRunner) DeletePortMapRuleForSvc(svc, excludeI string, targetIP netip.Addr, pm PortMap) error {
|
||
|
table := i.getIPTByAddr(targetIP)
|
||
|
args := argsForPortMapRule(svc, excludeI, targetIP, pm)
|
||
|
exists, err := table.Exists("nat", "PREROUTING", args...)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("error checking if rule exists: %w", err)
|
||
|
}
|
||
|
if exists {
|
||
|
return table.Delete("nat", "PREROUTING", args...)
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// DeleteSvc constructs all possible rules that would have been created by
|
||
|
// EnsurePortMapRuleForSvc from the provided args and ensures that each one that
|
||
|
// exists is deleted.
|
||
|
func (i *iptablesRunner) DeleteSvc(svc, tun string, targetIPs []netip.Addr, pms []PortMap) error {
|
||
|
for _, tip := range targetIPs {
|
||
|
for _, pm := range pms {
|
||
|
if err := i.DeletePortMapRuleForSvc(svc, tun, tip, pm); err != nil {
|
||
|
return fmt.Errorf("error deleting rule: %w", err)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func argsForPortMapRule(svc, excludeI string, targetIP netip.Addr, pm PortMap) []string {
|
||
|
c := commentForSvc(svc, pm)
|
||
|
return []string{
|
||
|
"!", "-i", excludeI,
|
||
|
"-p", pm.Protocol,
|
||
|
"--dport", fmt.Sprintf("%d", pm.MatchPort),
|
||
|
"-m", "comment", "--comment", c,
|
||
|
"-j", "DNAT",
|
||
|
"--to-destination", fmt.Sprintf("%v:%v", targetIP, pm.TargetPort),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// commentForSvc generates a comment to be added to an iptables DNAT rule for a
|
||
|
// service. This is for iptables debugging/readability purposes only.
|
||
|
func commentForSvc(svc string, pm PortMap) string {
|
||
|
return fmt.Sprintf("%s:%s:%d -> %s:%d", svc, pm.Protocol, pm.MatchPort, pm.Protocol, pm.TargetPort)
|
||
|
}
|