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 }