mirror of
				https://github.com/tailscale/tailscale.git
				synced 2025-10-31 03:49:52 +00:00 
			
		
		
		
	 01a7726cf7
			
		
	
	01a7726cf7
	
	
	
		
			
			cmd/containerboot,cmd/k8s-operator: enable IPv6 for fqdn egress proxies Don't skip installing egress forwarding rules for IPv6 (as long as the host supports IPv6), and set headless services `ipFamilyPolicy` to `PreferDualStack` to optionally enable both IP families when possible. Note that even with `PreferDualStack` set, testing a dual-stack GKE cluster with the default DNS setup of kube-dns did not correctly set both A and AAAA records for the headless service, and instead only did so when switching the cluster DNS to Cloud DNS. For both IPv4 and IPv6 to work simultaneously in a dual-stack cluster, we require headless services to return both A and AAAA records. If the host doesn't support IPv6 but the FQDN specified only has IPv6 addresses available, containerboot will exit with error code 1 and an error message because there is no viable egress route. Fixes #12215 Signed-off-by: Tom Proctor <tomhjp@users.noreply.github.com>
		
			
				
	
	
		
			143 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			143 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright (c) Tailscale Inc & AUTHORS
 | |
| // SPDX-License-Identifier: BSD-3-Clause
 | |
| 
 | |
| //go:build linux
 | |
| 
 | |
| package linuxfw
 | |
| 
 | |
| import (
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"os"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| )
 | |
| 
 | |
| type fakeIPTables struct {
 | |
| 	n map[string][]string
 | |
| }
 | |
| 
 | |
| type fakeRule struct {
 | |
| 	table, chain string
 | |
| 	args         []string
 | |
| }
 | |
| 
 | |
| func newFakeIPTables() *fakeIPTables {
 | |
| 	return &fakeIPTables{
 | |
| 		n: map[string][]string{
 | |
| 			"filter/INPUT":    nil,
 | |
| 			"filter/OUTPUT":   nil,
 | |
| 			"filter/FORWARD":  nil,
 | |
| 			"nat/PREROUTING":  nil,
 | |
| 			"nat/OUTPUT":      nil,
 | |
| 			"nat/POSTROUTING": nil,
 | |
| 			"mangle/FORWARD":  nil,
 | |
| 		},
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (n *fakeIPTables) Insert(table, chain string, pos int, args ...string) error {
 | |
| 	k := table + "/" + chain
 | |
| 	if rules, ok := n.n[k]; ok {
 | |
| 		if pos > len(rules)+1 {
 | |
| 			return fmt.Errorf("bad position %d in %s", pos, k)
 | |
| 		}
 | |
| 		rules = append(rules, "")
 | |
| 		copy(rules[pos:], rules[pos-1:])
 | |
| 		rules[pos-1] = strings.Join(args, " ")
 | |
| 		n.n[k] = rules
 | |
| 	} else {
 | |
| 		return fmt.Errorf("unknown table/chain %s", k)
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (n *fakeIPTables) Append(table, chain string, args ...string) error {
 | |
| 	k := table + "/" + chain
 | |
| 	return n.Insert(table, chain, len(n.n[k])+1, args...)
 | |
| }
 | |
| 
 | |
| func (n *fakeIPTables) Exists(table, chain string, args ...string) (bool, error) {
 | |
| 	k := table + "/" + chain
 | |
| 	if rules, ok := n.n[k]; ok {
 | |
| 		for _, rule := range rules {
 | |
| 			if rule == strings.Join(args, " ") {
 | |
| 				return true, nil
 | |
| 			}
 | |
| 		}
 | |
| 		return false, nil
 | |
| 	} else {
 | |
| 		return false, fmt.Errorf("unknown table/chain %s", k)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (n *fakeIPTables) Delete(table, chain string, args ...string) error {
 | |
| 	k := table + "/" + chain
 | |
| 	if rules, ok := n.n[k]; ok {
 | |
| 		for i, rule := range rules {
 | |
| 			if rule == strings.Join(args, " ") {
 | |
| 				rules = append(rules[:i], rules[i+1:]...)
 | |
| 				n.n[k] = rules
 | |
| 				return nil
 | |
| 			}
 | |
| 		}
 | |
| 		return fmt.Errorf("delete of unknown rule %q from %s", strings.Join(args, " "), k)
 | |
| 	} else {
 | |
| 		return fmt.Errorf("unknown table/chain %s", k)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (n *fakeIPTables) List(table, chain string) ([]string, error) {
 | |
| 	k := table + "/" + chain
 | |
| 	if rules, ok := n.n[k]; ok {
 | |
| 		return rules, nil
 | |
| 	} else {
 | |
| 		return nil, fmt.Errorf("unknown table/chain %s", k)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (n *fakeIPTables) ClearChain(table, chain string) error {
 | |
| 	k := table + "/" + chain
 | |
| 	if _, ok := n.n[k]; ok {
 | |
| 		n.n[k] = nil
 | |
| 		return nil
 | |
| 	} else {
 | |
| 		return errors.New("exitcode:1")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (n *fakeIPTables) NewChain(table, chain string) error {
 | |
| 	k := table + "/" + chain
 | |
| 	if _, ok := n.n[k]; ok {
 | |
| 		return fmt.Errorf("table/chain %s already exists", k)
 | |
| 	}
 | |
| 	n.n[k] = nil
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (n *fakeIPTables) DeleteChain(table, chain string) error {
 | |
| 	k := table + "/" + chain
 | |
| 	if rules, ok := n.n[k]; ok {
 | |
| 		if len(rules) != 0 {
 | |
| 			return fmt.Errorf("table/chain %s is not empty", k)
 | |
| 		}
 | |
| 		delete(n.n, k)
 | |
| 		return nil
 | |
| 	} else {
 | |
| 		return fmt.Errorf("unknown table/chain %s", k)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func NewFakeIPTablesRunner() *iptablesRunner {
 | |
| 	ipt4 := newFakeIPTables()
 | |
| 	v6Available := false
 | |
| 	var ipt6 iptablesInterface
 | |
| 	if use6, err := strconv.ParseBool(os.Getenv("TS_TEST_FAKE_NETFILTER_6")); use6 || err != nil {
 | |
| 		ipt6 = newFakeIPTables()
 | |
| 		v6Available = true
 | |
| 	}
 | |
| 
 | |
| 	iptr := &iptablesRunner{ipt4, ipt6, v6Available, v6Available, v6Available}
 | |
| 	return iptr
 | |
| }
 |