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 }