// EnsureRule implements Firewall interface. It schedules given rule for // transition in given state and stores it in firewall store. func (i *IPTsaveFirewall) EnsureRule(rule FirewallRule, opType RuleState) error { log.Infof("In EnsureRule() with firewall rule %s %s", opType.String(), rule.GetBody()) var ruleExists bool // This firewall only manages filtering rules so it only operates // on `filter` table. table := i.DesiredState.TableByName("filter") if table == nil { return fmt.Errorf("In EnsureRule() firewall doesn't have filter table") } // Convert iptables rule from Firewall interface into iptsave.IPrule. tempChain := iptsave.ParseRule(bytes.NewReader([]byte(rule.GetBody()))) ipRule := tempChain.Rules[0] // Ensure that base chain of the rule is defined in desired state. chain := table.ChainByName(tempChain.Name) if chain == nil { table.Chains = append(table.Chains, tempChain) chain = tempChain // we just added a chain with our rule // into the filter table so we know that // target rule is in the table. ruleExists = true } // If we didn't put that rule in the table ourselves yet then // try to find it in existing table. if !ruleExists { ruleExists = chain.RuleInChain(ipRule) } if ruleExists { switch opType { case EnsureAbsent: log.Infof("In EnsureRule - rule %s exists in current state, removing", rule.GetBody()) chain.DeleteRule(ipRule) default: log.Infof("In EnsureRule - nothing to do %s", rule.GetBody()) } } else { log.Infof("In EnsureRule - rule %s doesn't exist is current state, %s", rule.GetBody(), opType.String()) switch opType { case EnsureLast: chain.AppendRule(ipRule) case EnsureFirst: chain.InsertRule(0, ipRule) default: log.Infof("In EnsureRule - nothing to do %s", rule.GetBody()) } } return nil }
// makeUndoRule checks if given rule is present in current state and if so it generates // an undo rule in desired state. func makeUndoRule(rule FirewallRule, tableCurrent, tableDesired *iptsave.IPtable) { // If rule exists in current state then we want to delete it // in order to do so we will generate an `undo` rule and schedule // the `undo` rule for installation by putting it in desired state. // e.g. current rule // "-A INPUT -j DROP" // `undo` rule in desired state is // "-D INPUT -j DROP" // convert iptables rule from Firewall interface into iptsave.IPrule tempChain := iptsave.ParseRule(bytes.NewReader([]byte(rule.GetBody()))) ipRule := tempChain.Rules[0] // check if chain exists in current iptables config chain := tableCurrent.ChainByName(tempChain.Name) if chain == nil { // if chain isn't in iptables config then none of the rules are // just skip them return } // scheduling rule deletion if chain.RuleInChain(ipRule) { ipRule.RenderState = iptsave.RenderDeleteRule // check if base chain exists in DesiredState chain = tableDesired.ChainByName(tempChain.Name) if chain == nil { // if not then create a new one chain = &iptsave.IPchain{Name: tempChain.Name, Policy: "-"} tableDesired.Chains = append(tableDesired.Chains, chain) } chain.AppendRule(ipRule) // if rule targets a chain that chain must be defined // in an iptables table // It's hard to tell iptables chain from custom modules // in iptables rule action, like `-j RESET` could be a jump // to RESET chain or call to custom module with RESET name. // Here, strategy is to check current state for chain with // name that corresponds to current rule action target, // if such chain exist then add it in desired state as well // otherwise assume the call to custom module. // ipRule.Action.Body is the 'target' of a rule, typically the name of another chain. targetChain := tableCurrent.ChainByName(ipRule.Action.Body) if targetChain != nil { if inDesiredStateAlready := tableDesired.ChainByName(ipRule.Action.Body); inDesiredStateAlready == nil { tableDesired.Chains = append(tableDesired.Chains, targetChain) } } } }