github.com/aporeto-inc/trireme-lib@v10.358.0+incompatible/controller/pkg/aclprovider/iptablesprovider_windows.go (about)

     1  // +build windows
     2  
     3  package provider
     4  
     5  import (
     6  	"bytes"
     7  	"fmt"
     8  	"math"
     9  	"strings"
    10  	"syscall"
    11  	"unsafe"
    12  
    13  	winipt "go.aporeto.io/enforcerd/trireme-lib/controller/internal/windows"
    14  	"go.aporeto.io/enforcerd/trireme-lib/utils/frontman"
    15  	"go.uber.org/zap"
    16  )
    17  
    18  // IptablesProvider is an abstraction of all the methods an implementation of userspace
    19  // iptables need to provide.
    20  type IptablesProvider interface {
    21  	BaseIPTables
    22  	// Commit will commit changes if it is a batch provider.
    23  	Commit() error
    24  	// RetrieveTable allows a caller to retrieve the final table.
    25  	RetrieveTable() map[string]map[string][]string
    26  	// ResetRules resets the rules to a state where rules with the substring subs are removed
    27  	ResetRules(subs string) error
    28  }
    29  
    30  // BaseIPTables is the base interface of iptables functions.
    31  type BaseIPTables interface {
    32  	// Append apends a rule to chain of table
    33  	Append(table, chain string, rulespec ...string) error
    34  	// Insert inserts a rule to a chain of table at the required pos
    35  	Insert(table, chain string, pos int, rulespec ...string) error
    36  	// Delete deletes a rule of a chain in the given table
    37  	Delete(table, chain string, rulespec ...string) error
    38  	// ListChains lists all the chains associated with a table
    39  	ListChains(table string) ([]string, error)
    40  	// ClearChain clears a chain in a table
    41  	ClearChain(table, chain string) error
    42  	// DeleteChain deletes a chain in the table. There should be no references to this chain
    43  	DeleteChain(table, chain string) error
    44  	// NewChain creates a new chain
    45  	NewChain(table, chain string) error
    46  	// ListRules lists the rules in the table/chain passed to it
    47  	ListRules(table, chain string) ([]string, error)
    48  }
    49  
    50  // BatchProvider uses iptables-restore to program ACLs
    51  type BatchProvider struct {
    52  	ipv4 bool
    53  }
    54  
    55  // NewGoIPTablesProviderV4 returns an IptablesProvider interface based on the go-iptables
    56  // external package.
    57  func NewGoIPTablesProviderV4(batchTables []string, customChain string) (IptablesProvider, error) {
    58  	return &BatchProvider{ipv4: true}, nil
    59  }
    60  
    61  // NewGoIPTablesProviderV6 returns an IptablesProvider interface based on the go-iptables
    62  // external package.
    63  func NewGoIPTablesProviderV6(batchTables []string, customChain string) (IptablesProvider, error) {
    64  	return &BatchProvider{ipv4: false}, nil
    65  }
    66  
    67  // NewCustomBatchProvider is a custom batch provider wher the downstream
    68  // iptables utility is provided by the caller. Very useful for testing
    69  // the ACL functions with a mock.
    70  func NewCustomBatchProvider(ipt BaseIPTables, commit func(buf *bytes.Buffer) error, batchTables []string) *BatchProvider {
    71  	return &BatchProvider{ipv4: true}
    72  }
    73  
    74  // helper function for passing args to frontman api
    75  func boolToUint8(b bool) uint8 {
    76  	if b {
    77  		return 1
    78  	}
    79  	return 0
    80  }
    81  
    82  const ipv4ChainSuffix = "-v4"
    83  const ipv6ChainSuffix = "-v6"
    84  
    85  func (b *BatchProvider) fixChainName(chain string) string {
    86  	if strings.HasSuffix(chain, ipv4ChainSuffix) || strings.HasSuffix(chain, ipv6ChainSuffix) {
    87  		return chain
    88  	}
    89  	if b.ipv4 {
    90  		chain += ipv4ChainSuffix
    91  	} else {
    92  		chain += ipv6ChainSuffix
    93  	}
    94  	return chain
    95  }
    96  
    97  func (b *BatchProvider) fixRuleSpec(rulespec []string) []string {
    98  	// When a rule is referencing another chain, then we need to add the IP version number.
    99  	length := len(rulespec)
   100  	for index, part := range rulespec {
   101  		nextIndex := index + 1
   102  		if part == "-j" && nextIndex < length {
   103  			switch rulespec[nextIndex] {
   104  			case "NFQUEUE":
   105  			case "NFQUEUE_FORCE":
   106  			case "REDIRECT":
   107  			case "ACCEPT":
   108  			case "ACCEPT_ONCE":
   109  			case "DROP":
   110  			case "MARK":
   111  			case "CONNMARK":
   112  			case "NFLOG":
   113  			default:
   114  				rulespec[nextIndex] = b.fixChainName(rulespec[nextIndex])
   115  			}
   116  		}
   117  	}
   118  	return rulespec
   119  }
   120  
   121  // Append will append the provided rule to the local cache or call
   122  // directly the iptables command depending on the table.
   123  func (b *BatchProvider) Append(table, chain string, rulespec ...string) error {
   124  
   125  	chain = b.fixChainName(chain)
   126  	rulespec = b.fixRuleSpec(rulespec)
   127  
   128  	zap.L().Debug(fmt.Sprintf("add rule %s to table/chain %s/%s", strings.Join(rulespec, " "), table, chain))
   129  
   130  	winRuleSpec, err := winipt.ParseRuleSpec(rulespec...)
   131  	if err != nil {
   132  		return err
   133  	}
   134  
   135  	criteriaID := strings.Join(rulespec, " ")
   136  
   137  	argRuleSpec := frontman.RuleSpec{
   138  		Action:    uint8(winRuleSpec.Action),
   139  		Log:       boolToUint8(winRuleSpec.Log),
   140  		GroupID:   uint32(winRuleSpec.GroupID),
   141  		ProxyPort: uint16(winRuleSpec.ProxyPort),
   142  		Mark:      uint32(winRuleSpec.Mark),
   143  		ProcessID: uint64(winRuleSpec.ProcessID),
   144  	}
   145  
   146  	if b.ipv4 {
   147  		argRuleSpec.IPVersionMatch = frontman.IPVersion4
   148  	} else {
   149  		argRuleSpec.IPVersionMatch = frontman.IPVersion6
   150  	}
   151  
   152  	if winRuleSpec.FlowMark != 0 {
   153  		argRuleSpec.FlowMark = uint32(winRuleSpec.FlowMark)
   154  		if winRuleSpec.FlowMarkNoMatch {
   155  			argRuleSpec.FlowMarkMatchType = frontman.MatchTypeNoMatch
   156  		} else {
   157  			argRuleSpec.FlowMarkMatchType = frontman.MatchTypeMatch
   158  		}
   159  	}
   160  
   161  	if winRuleSpec.TCPFlagsSpecified {
   162  		argRuleSpec.TCPFlagsSpecified = 1
   163  		argRuleSpec.TCPFlags = winRuleSpec.TCPFlags
   164  		argRuleSpec.TCPFlagsMask = winRuleSpec.TCPFlagsMask
   165  	}
   166  
   167  	if winRuleSpec.TCPOptionSpecified {
   168  		argRuleSpec.TCPOptionSpecified = 1
   169  		argRuleSpec.TCPOption = winRuleSpec.TCPOption
   170  	}
   171  
   172  	if winRuleSpec.Protocol > 0 && winRuleSpec.Protocol < math.MaxUint8 {
   173  		argRuleSpec.ProtocolSpecified = 1
   174  		argRuleSpec.Protocol = uint8(winRuleSpec.Protocol)
   175  	}
   176  	if len(winRuleSpec.MatchSrcPort) > 0 {
   177  		argRuleSpec.SrcPortCount = int32(len(winRuleSpec.MatchSrcPort))
   178  		srcPorts := make([]frontman.PortRange, argRuleSpec.SrcPortCount)
   179  		for i, portRange := range winRuleSpec.MatchSrcPort {
   180  			srcPorts[i] = frontman.PortRange{PortStart: uint16(portRange.Start), PortEnd: uint16(portRange.End)}
   181  		}
   182  		argRuleSpec.SrcPorts = &srcPorts[0]
   183  	}
   184  	if len(winRuleSpec.MatchDstPort) > 0 {
   185  		argRuleSpec.DstPortCount = int32(len(winRuleSpec.MatchDstPort))
   186  		dstPorts := make([]frontman.PortRange, argRuleSpec.DstPortCount)
   187  		for i, portRange := range winRuleSpec.MatchDstPort {
   188  			dstPorts[i] = frontman.PortRange{PortStart: uint16(portRange.Start), PortEnd: uint16(portRange.End)}
   189  		}
   190  		argRuleSpec.DstPorts = &dstPorts[0]
   191  	}
   192  	if len(winRuleSpec.MatchBytes) > 0 {
   193  		if winRuleSpec.MatchBytesNoMatch {
   194  			argRuleSpec.BytesNoMatch = 1
   195  		}
   196  		argRuleSpec.BytesMatchStart = frontman.BytesMatchStartPayload
   197  		argRuleSpec.BytesMatchOffset = int32(winRuleSpec.MatchBytesOffset)
   198  		argRuleSpec.BytesMatchSize = int32(len(winRuleSpec.MatchBytes))
   199  		argRuleSpec.BytesMatch = &winRuleSpec.MatchBytes[0]
   200  	}
   201  	if winRuleSpec.LogPrefix != "" {
   202  		argRuleSpec.LogPrefix = uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(winRuleSpec.LogPrefix))) // nolint:staticcheck
   203  	}
   204  	if winRuleSpec.ProcessID > 0 {
   205  		if winRuleSpec.ProcessIncludeChildrenOnly {
   206  			argRuleSpec.ProcessFlags = frontman.ProcessMatchChildren
   207  		} else {
   208  			argRuleSpec.ProcessFlags = frontman.ProcessMatchProcess
   209  			if winRuleSpec.ProcessIncludeChildren {
   210  				argRuleSpec.ProcessFlags |= frontman.ProcessMatchChildren
   211  			}
   212  		}
   213  	}
   214  	if len(winRuleSpec.IcmpMatch) > 0 {
   215  		argRuleSpec.IcmpRangeCount = int32(len(winRuleSpec.IcmpMatch))
   216  		icmpRanges := make([]frontman.IcmpRange, argRuleSpec.IcmpRangeCount)
   217  		for i, im := range winRuleSpec.IcmpMatch {
   218  			r := frontman.IcmpRange{}
   219  			if !im.Nomatch {
   220  				r.IcmpTypeSpecified = 1
   221  				r.IcmpType = uint8(im.IcmpType)
   222  				if im.IcmpCodeRange != nil {
   223  					r.IcmpCodeSpecified = 1
   224  					r.IcmpCodeLower = uint8(im.IcmpCodeRange.Start)
   225  					r.IcmpCodeUpper = uint8(im.IcmpCodeRange.End)
   226  				}
   227  			}
   228  			icmpRanges[i] = r
   229  		}
   230  		argRuleSpec.IcmpRanges = &icmpRanges[0]
   231  	}
   232  	argIpsetRuleSpecs := make([]frontman.IpsetRuleSpec, len(winRuleSpec.MatchSet))
   233  	for i, matchSet := range winRuleSpec.MatchSet {
   234  		argIpsetRuleSpecs[i].NotIpset = boolToUint8(matchSet.MatchSetNegate)
   235  		argIpsetRuleSpecs[i].IpsetDstIP = boolToUint8(matchSet.MatchSetDstIP)
   236  		argIpsetRuleSpecs[i].IpsetDstPort = boolToUint8(matchSet.MatchSetDstPort)
   237  		argIpsetRuleSpecs[i].IpsetSrcIP = boolToUint8(matchSet.MatchSetSrcIP)
   238  		argIpsetRuleSpecs[i].IpsetSrcPort = boolToUint8(matchSet.MatchSetSrcPort)
   239  		argIpsetRuleSpecs[i].IpsetName = uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(matchSet.MatchSetName))) // nolint:staticcheck
   240  	}
   241  
   242  	if winRuleSpec.GotoFilterName != "" {
   243  		argRuleSpec.GotoFilterName = uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(winRuleSpec.GotoFilterName))) // nolint:staticcheck
   244  	}
   245  
   246  	return frontman.Wrapper.AppendFilterCriteria(chain, criteriaID, &argRuleSpec, argIpsetRuleSpecs)
   247  }
   248  
   249  // Insert will insert the rule in the corresponding position in the local
   250  // cache or call the corresponding iptables command, depending on the table.
   251  func (b *BatchProvider) Insert(table, chain string, pos int, rulespec ...string) error {
   252  	chain = b.fixChainName(chain)
   253  	zap.L().Debug(fmt.Sprintf("Insert not expected for table %s and chain %s", table, chain))
   254  	return nil
   255  }
   256  
   257  // Delete will delete the rule from the local cache or the system.
   258  func (b *BatchProvider) Delete(table, chain string, rulespec ...string) error {
   259  	chain = b.fixChainName(chain)
   260  	rulespec = b.fixRuleSpec(rulespec)
   261  	criteriaID := strings.Join(rulespec, " ")
   262  	zap.L().Debug(fmt.Sprintf("delete rule %s from table/chain %s/%s", criteriaID, table, chain))
   263  	return frontman.Wrapper.DeleteFilterCriteria(chain, criteriaID)
   264  }
   265  
   266  // ListChains will provide a list of the current chains.
   267  func (b *BatchProvider) ListChains(table string) ([]string, error) {
   268  	var outbound bool
   269  	if strings.HasPrefix(table, "O") || strings.HasPrefix(table, "o") {
   270  		outbound = true
   271  	} else if strings.HasPrefix(table, "I") || strings.HasPrefix(table, "i") {
   272  		outbound = false
   273  	} else {
   274  		return nil, fmt.Errorf("'%s' is not a valid table for ListChains", table)
   275  	}
   276  
   277  	return frontman.Wrapper.GetFilterList(outbound)
   278  }
   279  
   280  // ClearChain will clear the chains.
   281  func (b *BatchProvider) ClearChain(table, chain string) error {
   282  	chain = b.fixChainName(chain)
   283  	return frontman.Wrapper.EmptyFilter(chain)
   284  }
   285  
   286  // DeleteChain will delete the chains.
   287  func (b *BatchProvider) DeleteChain(table, chain string) error {
   288  	chain = b.fixChainName(chain)
   289  	return frontman.Wrapper.DestroyFilter(chain)
   290  }
   291  
   292  // NewChain creates a new chain.
   293  func (b *BatchProvider) NewChain(table, chain string) error {
   294  	chain = b.fixChainName(chain)
   295  	var outbound bool
   296  	if strings.HasPrefix(table, "O") || strings.HasPrefix(table, "o") {
   297  		outbound = true
   298  	} else if strings.HasPrefix(table, "I") || strings.HasPrefix(table, "i") {
   299  		outbound = false
   300  	} else {
   301  		return fmt.Errorf("'%s' is not a valid table for NewChain", table)
   302  	}
   303  
   304  	// A goto filter is outside the normal filters and a rule can jump to a "goto filter"
   305  	// So any filter that has -Net or -App is a goto filter.
   306  	isGotoFilter := false
   307  	if strings.Contains(chain, "-Net") || strings.Contains(chain, "-App") {
   308  		isGotoFilter = true
   309  	}
   310  	return frontman.Wrapper.AppendFilter(outbound, chain, isGotoFilter)
   311  }
   312  
   313  // Commit commits the rules to the system
   314  func (b *BatchProvider) Commit() error {
   315  	// does nothing
   316  	return nil
   317  }
   318  
   319  // RetrieveTable allows a caller to retrieve the final table. Mostly
   320  // needed for debuging and unit tests.
   321  func (b *BatchProvider) RetrieveTable() map[string]map[string][]string {
   322  	// not applicable for windows
   323  	return map[string]map[string][]string{}
   324  }
   325  
   326  // ResetRules returns nil in windows
   327  func (b *BatchProvider) ResetRules(subs string) error {
   328  	// does nothing
   329  	return nil
   330  }
   331  
   332  // ListRules lists the rules in the table/chain passed to it
   333  func (b *BatchProvider) ListRules(table, chain string) ([]string, error) {
   334  	// This is is unimplemented on windows
   335  	return []string{}, nil
   336  }