github.com/Azure/aad-pod-identity@v1.8.17/pkg/nmi/iptables/iptables.go (about)

     1  package iptables
     2  
     3  import (
     4  	"errors"
     5  	"strings"
     6  
     7  	"github.com/coreos/go-iptables/iptables"
     8  	"k8s.io/klog/v2"
     9  )
    10  
    11  var (
    12  	tablename       = "nat"
    13  	customchainname = "aad-metadata"
    14  	localhost       = "127.0.0.1/32"
    15  )
    16  
    17  // AddCustomChain adds the rule to the host's nat table custom chain
    18  // all tcp requests NOT originating from localhost destined to
    19  // destIp:destPort are routed to targetIP:targetPort
    20  func AddCustomChain(destIP, destPort, targetip, targetport string) error {
    21  	if destIP == "" {
    22  		return errors.New("destIP must be set")
    23  	}
    24  	if destPort == "" {
    25  		return errors.New("destPort must be set")
    26  	}
    27  	if targetip == "" {
    28  		return errors.New("targetip must be set")
    29  	}
    30  	if targetport == "" {
    31  		return errors.New("targetport must be set")
    32  	}
    33  
    34  	ipt, err := iptables.New()
    35  	if err != nil {
    36  		return err
    37  	}
    38  	if err := ensureCustomChain(ipt, destIP, destPort, targetip, targetport); err != nil {
    39  		return err
    40  	}
    41  	if err := placeCustomChainInChain(ipt, tablename, "PREROUTING"); err != nil {
    42  		return err
    43  	}
    44  
    45  	return nil
    46  }
    47  
    48  // LogCustomChain logs added rules to the custom chain
    49  func LogCustomChain() error {
    50  	ipt, err := iptables.New()
    51  	if err != nil {
    52  		return err
    53  	}
    54  	rules, err := ipt.List(tablename, customchainname)
    55  	if err != nil {
    56  		return err
    57  	}
    58  	klog.V(5).Infof("rules for table(%s) chain(%s) rules(%+v)", tablename, customchainname, strings.Join(rules, ", "))
    59  
    60  	return nil
    61  }
    62  
    63  // iptables -t nat -I "chain" 1 -j "customchainname"
    64  func placeCustomChainInChain(ipt *iptables.IPTables, table, chain string) error {
    65  	exists, err := ipt.Exists(table, chain, "-j", customchainname)
    66  	if err != nil || !exists {
    67  		if err := ipt.Insert(table, chain, 1, "-j", customchainname); err != nil {
    68  			return err
    69  		}
    70  	}
    71  
    72  	return nil
    73  }
    74  
    75  func ensureCustomChain(ipt *iptables.IPTables, destIP, destPort, targetip, targetport string) error {
    76  	rules, err := ipt.List(tablename, customchainname)
    77  	if err != nil {
    78  		err = ipt.NewChain(tablename, customchainname)
    79  		if err != nil {
    80  			return err
    81  		}
    82  	}
    83  
    84  	/*
    85  		iptables -t nat -S aad-metadata returns 3 rules
    86  			-N aad-metadata
    87  			-A aad-metadata ! -s 127.0.0.1/32 -d 169.254.169.254/32 -p tcp -m tcp --dport 80 -j DNAT --to-destination 127.0.0.1:<nmi port>
    88  			-A aad-metadata -j RETURN
    89  
    90  		For this reason we check if the length of rules is 3. If not 3, then we flush and create chain again.
    91  	*/
    92  
    93  	expectedRules := map[string]struct{}{
    94  		"-N aad-metadata": {},
    95  		"-A aad-metadata ! -s 127.0.0.1/32 -d " + destIP + "/32 -p tcp -m tcp --dport " + destPort + " -j DNAT --to-destination " + targetip + ":" + targetport: {},
    96  		"-A aad-metadata -j RETURN": {},
    97  	}
    98  
    99  	matchingRules := 0
   100  	// ensure all the rules are as expected with the right IPs
   101  	// if any rule has been changed, then we need to flush the
   102  	// entire chain and reconcile with the correct IPs
   103  	for _, rule := range rules {
   104  		if _, ok := expectedRules[rule]; !ok {
   105  			break
   106  		}
   107  		matchingRules++
   108  	}
   109  	// all the required rules exist, so no need to flush custom chain
   110  	if matchingRules == len(expectedRules) {
   111  		return nil
   112  	}
   113  
   114  	if err := flushCreateCustomChainrules(ipt, destIP, destPort,
   115  		targetip, targetport); err != nil {
   116  		return err
   117  	}
   118  
   119  	return nil
   120  }
   121  
   122  func flushCreateCustomChainrules(ipt *iptables.IPTables, destIP, destPort, targetip, targetport string) error {
   123  	klog.Warning("flushing iptables to add aad-metadata custom chains")
   124  	if err := ipt.ClearChain(tablename, customchainname); err != nil {
   125  		return err
   126  	}
   127  	if err := ipt.AppendUnique(
   128  		tablename, customchainname, "-p", "tcp", "!", "-s", localhost, "-d", destIP, "--dport", destPort,
   129  		"-j", "DNAT", "--to-destination", targetip+":"+targetport); err != nil {
   130  		return err
   131  	}
   132  	if err := ipt.AppendUnique(
   133  		tablename, customchainname, "-j", "RETURN"); err != nil {
   134  		return err
   135  	}
   136  
   137  	return nil
   138  }
   139  
   140  // DeleteCustomChain removes the custom chain aad-metadata reference from PREROUTING
   141  // chain and then removes the chain aad-metadata from nat table
   142  func DeleteCustomChain() error {
   143  	ipt, err := iptables.New()
   144  	if err != nil {
   145  		return err
   146  	}
   147  	if err := removeCustomChainReference(ipt, tablename, "PREROUTING"); err != nil {
   148  		return err
   149  	}
   150  	if err := removeCustomChain(ipt, tablename); err != nil {
   151  		return err
   152  	}
   153  	return nil
   154  }
   155  
   156  // removeCustomChainReference - iptables -t "table" -D "chain" -j "customchainname"
   157  func removeCustomChainReference(ipt *iptables.IPTables, table, chain string) error {
   158  	exists, err := ipt.Exists(table, chain, "-j", customchainname)
   159  	if err == nil && exists {
   160  		return ipt.Delete(table, chain, "-j", customchainname)
   161  	}
   162  	return nil
   163  }
   164  
   165  // removeCustomChain -  flush and then delete custom chain
   166  // iptables -t "table" -F "customchainname"
   167  // iptables -t "table" -X "customchainname"
   168  func removeCustomChain(ipt *iptables.IPTables, table string) error {
   169  	if err := ipt.ClearChain(table, customchainname); err != nil {
   170  		return err
   171  	}
   172  	if err := ipt.DeleteChain(table, customchainname); err != nil {
   173  		return err
   174  	}
   175  	return nil
   176  }