github.com/aporeto-inc/trireme-lib@v10.358.0+incompatible/controller/internal/supervisor/iptablesctrl/acls_windows.go (about)

     1  // +build windows
     2  
     3  package iptablesctrl
     4  
     5  import (
     6  	"fmt"
     7  	"os"
     8  	"strings"
     9  	"syscall"
    10  	"unsafe"
    11  
    12  	"github.com/kballard/go-shellquote"
    13  	"go.aporeto.io/enforcerd/trireme-lib/controller/constants"
    14  	winipt "go.aporeto.io/enforcerd/trireme-lib/controller/internal/windows"
    15  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/ipsetmanager"
    16  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/packet"
    17  
    18  	"go.aporeto.io/enforcerd/trireme-lib/common"
    19  	"go.uber.org/zap"
    20  )
    21  
    22  // discoverCnsAgentBootPID finds parent's parent pid on Windows.
    23  // needs to happen early, in case our mgr parent is relaunched.
    24  var discoverCnsAgentBootPID = func() int {
    25  	pppid, err := getGrandparentPid()
    26  	if err != nil {
    27  		zap.L().Error("Could not get CnsAgentBootPID", zap.Error(err))
    28  		return -1
    29  	}
    30  	return pppid
    31  }
    32  
    33  func getGrandparentPid() (int, error) {
    34  	ppid := os.Getppid()
    35  	if ppid <= 0 {
    36  		return -1, fmt.Errorf("getGrandparentPid failed to get ppid")
    37  	}
    38  	// from getProcessEntry in syscall_windows.go
    39  	snapshot, err := syscall.CreateToolhelp32Snapshot(syscall.TH32CS_SNAPPROCESS, 0)
    40  	if err != nil {
    41  		return -1, err
    42  	}
    43  	defer syscall.CloseHandle(snapshot) // nolint: errcheck
    44  	var procEntry syscall.ProcessEntry32
    45  	procEntry.Size = uint32(unsafe.Sizeof(procEntry))
    46  	if err = syscall.Process32First(snapshot, &procEntry); err != nil {
    47  		return -1, err
    48  	}
    49  	for {
    50  		if procEntry.ProcessID == uint32(ppid) {
    51  			return int(procEntry.ParentProcessID), nil
    52  		}
    53  		err = syscall.Process32Next(snapshot, &procEntry)
    54  		if err != nil {
    55  			return -1, err
    56  		}
    57  	}
    58  }
    59  
    60  func (i *iptables) aclSkipProto(proto string) bool {
    61  	return false
    62  }
    63  
    64  func (i *iptables) legacyPuChainRules(cfg *ACLInfo) ([][]string, bool) {
    65  	return nil, false
    66  }
    67  
    68  // create ipsets needed for Windows rules
    69  func (i *iptables) platformInit() error {
    70  
    71  	ipset := ipsetmanager.IPsetProvider()
    72  
    73  	cfg, err := i.newACLInfo(0, "", nil, 0)
    74  	if err != nil {
    75  		return err
    76  	}
    77  
    78  	existingSets, err := ipset.ListIPSets()
    79  	if err != nil {
    80  		return err
    81  	}
    82  
    83  	setExists := func(s string) bool {
    84  		for _, existing := range existingSets {
    85  			if existing == s {
    86  				return true
    87  			}
    88  		}
    89  		return false
    90  	}
    91  
    92  	if !setExists("TRI-v4-WindowsAllIPs") {
    93  		allIPsV4, err := ipset.NewIpset("TRI-v4-WindowsAllIPs", "hash:net", nil)
    94  		if err != nil {
    95  			return err
    96  		}
    97  		err = allIPsV4.Add("0.0.0.0/0", 0)
    98  		if err != nil {
    99  			return err
   100  		}
   101  	}
   102  
   103  	if !setExists("TRI-v6-WindowsAllIPs") {
   104  		allIPsV6, err := ipset.NewIpset("TRI-v6-WindowsAllIPs", "hash:net", nil)
   105  		if err != nil {
   106  			return err
   107  		}
   108  		err = allIPsV6.Add("::/0", 0)
   109  		if err != nil {
   110  			return err
   111  		}
   112  	}
   113  
   114  	if cfg.DNSServerIP != "" {
   115  		// TRI-v4-WindowsDNSServer is used in a global rule that applies to both IPv4/IPv6,
   116  		// but we need the TRI-v4 prefix on the name so that it is properly cleaned up
   117  		if !setExists("TRI-v4-WindowsDNSServer") {
   118  			dnsIPSet, err := ipset.NewIpset("TRI-v4-WindowsDNSServer", "hash:net", nil)
   119  			if err != nil {
   120  				return err
   121  			}
   122  			switch cfg.DNSServerIP {
   123  			case IPv4DefaultIP, IPv6DefaultIP:
   124  				// in the case of an all-network range (which is the default value), we need to allow all for ipv4+ipv6
   125  				err = dnsIPSet.Add(IPv4DefaultIP, 0)
   126  				if err != nil {
   127  					return err
   128  				}
   129  				err = dnsIPSet.Add(IPv6DefaultIP, 0)
   130  				if err != nil {
   131  					return err
   132  				}
   133  			default:
   134  				// for now, we add
   135  				err = dnsIPSet.Add(cfg.DNSServerIP, 0)
   136  				if err != nil {
   137  					return err
   138  				}
   139  			}
   140  		}
   141  	}
   142  
   143  	return nil
   144  }
   145  
   146  // addContainerChain for Windows
   147  func (i *iptables) addContainerChain(cfg *ACLInfo) error {
   148  	appChain := cfg.AppChain
   149  	netChain := cfg.NetChain
   150  	if err := i.impl.NewChain(appPacketIPTableContext, appChain); err != nil {
   151  		return fmt.Errorf("unable to add chain %s of context %s: %s", appChain, appPacketIPTableContext, err)
   152  	}
   153  	if err := i.impl.NewChain(netPacketIPTableContext, netChain); err != nil {
   154  		return fmt.Errorf("unable to add netchain %s of context %s: %s", netChain, netPacketIPTableContext, err)
   155  	}
   156  	return nil
   157  }
   158  
   159  // deletePUChains removes all the container specific chains and basic rules
   160  func (i *iptables) deletePUChains(cfg *ACLInfo) error {
   161  
   162  	if err := i.impl.ClearChain(appPacketIPTableContext, cfg.AppChain); err != nil {
   163  		zap.L().Warn("Failed to clear the container ack packets chain",
   164  			zap.String("appChain", cfg.AppChain),
   165  			zap.String("context", appPacketIPTableContext),
   166  			zap.Error(err),
   167  		)
   168  	}
   169  
   170  	if err := i.impl.DeleteChain(appPacketIPTableContext, cfg.AppChain); err != nil {
   171  		zap.L().Warn("Failed to delete the container ack packets chain",
   172  			zap.String("appChain", cfg.AppChain),
   173  			zap.String("context", appPacketIPTableContext),
   174  			zap.Error(err),
   175  		)
   176  	}
   177  
   178  	if err := i.impl.ClearChain(netPacketIPTableContext, cfg.NetChain); err != nil {
   179  		zap.L().Warn("Failed to clear the container net packets chain",
   180  			zap.String("netChain", cfg.NetChain),
   181  			zap.String("context", netPacketIPTableContext),
   182  			zap.Error(err),
   183  		)
   184  	}
   185  
   186  	if err := i.impl.DeleteChain(netPacketIPTableContext, cfg.NetChain); err != nil {
   187  		zap.L().Warn("Failed to delete the container net packets chain",
   188  			zap.String("netChain", cfg.NetChain),
   189  			zap.String("context", netPacketIPTableContext),
   190  			zap.Error(err),
   191  		)
   192  	}
   193  
   194  	return nil
   195  }
   196  
   197  // try to merge two acl rules (one log and one accept/drop) into one for Windows
   198  func makeTerminatingRuleFromPair(aclRule1, aclRule2 []string) *winipt.WindowsRuleSpec {
   199  
   200  	if aclRule1 == nil || aclRule2 == nil {
   201  		return nil
   202  	}
   203  	winRuleSpec1, err := winipt.ParseRuleSpec(aclRule1[2:]...)
   204  	if err != nil {
   205  		return nil
   206  	}
   207  	winRuleSpec2, err := winipt.ParseRuleSpec(aclRule2[2:]...)
   208  	if err != nil {
   209  		return nil
   210  	}
   211  
   212  	// save action/log properties, as long as one rule is an action and the other is nflog
   213  	action := 0
   214  	logPrefix := ""
   215  	groupID := 0
   216  	if action == 0 && winRuleSpec1.Action != 0 && winRuleSpec2.Log {
   217  		action = winRuleSpec1.Action
   218  		logPrefix = winRuleSpec2.LogPrefix
   219  		groupID = winRuleSpec2.GroupID
   220  	}
   221  	if action == 0 && winRuleSpec2.Action != 0 && winRuleSpec1.Log {
   222  		action = winRuleSpec2.Action
   223  		logPrefix = winRuleSpec1.LogPrefix
   224  		groupID = winRuleSpec1.GroupID
   225  	}
   226  	if action == 0 {
   227  		return nil
   228  	}
   229  
   230  	// if one is nflog and one is another action, and they are otherwise equal, then combine into one rule
   231  	winRuleSpec1.Log = false
   232  	winRuleSpec1.LogPrefix = ""
   233  	winRuleSpec1.GroupID = 0
   234  	winRuleSpec1.Action = 0
   235  	winRuleSpec2.Log = false
   236  	winRuleSpec2.LogPrefix = ""
   237  	winRuleSpec2.GroupID = 0
   238  	winRuleSpec2.Action = 0
   239  	if winRuleSpec1.Equal(winRuleSpec2) {
   240  		winRuleSpec1.Log = true
   241  		winRuleSpec1.LogPrefix = logPrefix
   242  		winRuleSpec1.GroupID = groupID
   243  		winRuleSpec1.Action = action
   244  		return winRuleSpec1
   245  	}
   246  	return nil
   247  }
   248  
   249  // take a parsed acl rule and clean it up, returning an acl rule in []string format
   250  func processWindowsACLRule(table, _ string, winRuleSpec *winipt.WindowsRuleSpec, cfg *ACLInfo, isAppAcls bool) ([]string, error) {
   251  	var chain string
   252  	if isAppAcls {
   253  		chain = cfg.AppChain
   254  	} else {
   255  		chain = cfg.NetChain
   256  	}
   257  	switch cfg.PUType {
   258  	case common.HostPU:
   259  	case common.HostNetworkPU:
   260  		if isAppAcls {
   261  			return nil, nil
   262  		}
   263  		switch winRuleSpec.Protocol {
   264  		case packet.IPProtocolTCP:
   265  		case packet.IPProtocolUDP:
   266  		default:
   267  			return nil, nil
   268  		}
   269  	case common.WindowsProcessPU:
   270  	default:
   271  		return nil, fmt.Errorf("unexpected Windows PU: %v", cfg.PUType)
   272  	}
   273  	rulespec, _ := winipt.MakeRuleSpecText(winRuleSpec, false)
   274  
   275  	rule, err := shellquote.Split(rulespec)
   276  	if err != nil {
   277  		return nil, err
   278  	}
   279  
   280  	return append([]string{table, chain}, rule...), nil
   281  }
   282  
   283  // while not strictly necessary now for Windows, we still try to combine a log (non-terminating rule) and another terminating rule.
   284  func transformACLRules(aclRules [][]string, cfg *ACLInfo, rulesBucket *rulesInfo, isAppAcls bool) [][]string {
   285  
   286  	// find the reverse rules and remove them.
   287  	// note: we assume that reverse rules are the ones we add for UDP established reverse flows.
   288  	// we handle this in the windows driver so we don't need a rule for it.
   289  	// again: our driver assumes that all UDP acl rules will have a reverse flow added.
   290  	if rulesBucket != nil {
   291  		for _, rr := range rulesBucket.ReverseRules {
   292  			revTable, revChain := rr[0], rr[1]
   293  			revRule, err := winipt.ParseRuleSpec(rr[2:]...)
   294  			if err != nil {
   295  				zap.L().Error("transformACLRules failed to parse reverse rule", zap.Error(err))
   296  				continue
   297  			}
   298  			found := false
   299  			for i, r := range aclRules {
   300  				rule, err := winipt.ParseRuleSpec(r[2:]...)
   301  				if err != nil {
   302  					zap.L().Error("transformACLRules failed to parse rule", zap.Error(err))
   303  					continue
   304  				}
   305  				table, chain := r[0], r[1]
   306  				if table == revTable && chain == revChain && rule.Equal(revRule) {
   307  					found = true
   308  					aclRules = append(aclRules[:i], aclRules[i+1:]...)
   309  					break
   310  				}
   311  			}
   312  			if !found {
   313  				zap.L().Warn("transformACLRules could not find reverse rule")
   314  			}
   315  		}
   316  	}
   317  
   318  	var result [][]string
   319  
   320  	// now in the loop, compare successive rules to see if they are equal, disregarding their action or log properties.
   321  	// if they are, then combine them into one rule.
   322  	var aclRule1, aclRule2 []string
   323  	for i := 0; i < len(aclRules) || aclRule1 != nil; i++ {
   324  		if aclRule1 == nil {
   325  			aclRule1 = aclRules[i]
   326  			i++
   327  		}
   328  		if i < len(aclRules) {
   329  			aclRule2 = aclRules[i]
   330  		}
   331  		table, chain := aclRule1[0], aclRule1[1]
   332  		winRule := makeTerminatingRuleFromPair(aclRule1, aclRule2)
   333  		if winRule == nil {
   334  			// not combinable, so work on rule 1
   335  			var err error
   336  			winRule, err = winipt.ParseRuleSpec(aclRule1[2:]...)
   337  			aclRule1 = aclRule2
   338  			aclRule2 = nil
   339  			if err != nil {
   340  				zap.L().Error("transformACLRules failed", zap.Error(err))
   341  				continue
   342  			}
   343  		} else {
   344  			aclRule1 = nil
   345  			aclRule2 = nil
   346  		}
   347  		// process rule
   348  		xformedRule, err := processWindowsACLRule(table, chain, winRule, cfg, isAppAcls)
   349  		if err != nil {
   350  			zap.L().Error("transformACLRules failed", zap.Error(err))
   351  			continue
   352  		}
   353  		if xformedRule != nil {
   354  			result = append(result, xformedRule)
   355  		}
   356  	}
   357  
   358  	if result == nil {
   359  		result = [][]string{}
   360  	}
   361  	return result
   362  }
   363  
   364  func (i *iptables) cleanACLs() error { // nolint
   365  	cfg, err := i.newACLInfo(0, "", nil, 0)
   366  	if err != nil {
   367  		return err
   368  	}
   369  
   370  	// First clear the nat rules
   371  	if err := i.removeGlobalHooks(cfg); err != nil {
   372  		zap.L().Error("unable to remove nat proxy rules")
   373  	}
   374  
   375  	// Clean Application Rules/Chains
   376  	i.cleanACLSection(appPacketIPTableContext, constants.ChainPrefix)
   377  	i.cleanACLSection(appProxyIPTableContext, constants.ChainPrefix)
   378  
   379  	i.impl.Commit() // nolint
   380  
   381  	// Always return nil here. No reason to block anything if cleans fail.
   382  	return nil
   383  }
   384  
   385  // cleanACLSection flushes and deletes all chains with Prefix - Trireme
   386  func (i *iptables) cleanACLSection(context, chainPrefix string) {
   387  
   388  	rules, err := i.impl.ListChains(context)
   389  	if err != nil {
   390  		zap.L().Warn("Failed to list chains",
   391  			zap.String("context", context),
   392  			zap.Error(err),
   393  		)
   394  	}
   395  
   396  	for _, rule := range rules {
   397  		if strings.Contains(rule, chainPrefix) {
   398  			if err := i.impl.ClearChain(context, rule); err != nil {
   399  				zap.L().Warn("Can not clear the chain",
   400  					zap.String("context", context),
   401  					zap.String("section", rule),
   402  					zap.Error(err),
   403  				)
   404  			}
   405  		}
   406  	}
   407  
   408  	for _, rule := range rules {
   409  		if strings.Contains(rule, chainPrefix) {
   410  			if err := i.impl.DeleteChain(context, rule); err != nil {
   411  				zap.L().Warn("Can not delete the chain",
   412  					zap.String("context", context),
   413  					zap.String("section", rule),
   414  					zap.Error(err),
   415  				)
   416  			}
   417  		}
   418  	}
   419  }
   420  
   421  func generateUDPACLRule() []string {
   422  	//-m", "string", "!", "--string", packet.UDPAuthMarker, "--offset", "4"
   423  	return []string{"-m", "string", "--string", "!", packet.UDPAuthMarker, "--offset", "6"}
   424  }
   425  
   426  func targetUDPNetworkClause(rule *aclIPset, targetUDPName string, ipMatchDirection string) []string {
   427  	if !strings.Contains(strings.Join(rule.Ports, ","), "53") {
   428  		return []string{"-m", "set", "!", "--match-set", targetUDPName, ipMatchDirection}
   429  	}
   430  	return []string{}
   431  }
   432  
   433  func connmarkUDPConnmarkClause() []string {
   434  	return []string{}
   435  }