Split iptables rules into append and prepend rules

This commit is contained in:
Alex Stockinger
2022-07-25 11:46:20 +02:00
parent 1921c6a212
commit c28a1a24d8
9 changed files with 144 additions and 56 deletions

View File

@@ -46,6 +46,20 @@ type fakeClient struct {
var _ Client = &fakeClient{}
func (f *fakeClient) Insert(table, chain string, pos int, spec ...string) error {
atomic.AddUint64(&f.calls, 1)
exists, err := f.Exists(table, chain, spec...)
if err != nil {
return err
}
if exists {
return nil
}
// FIXME obey pos!
f.storage = append([]Rule{&rule{table: table, chain: chain, spec: spec}}, f.storage...)
return nil
}
func (f *fakeClient) AppendUnique(table, chain string, spec ...string) error {
atomic.AddUint64(&f.calls, 1)
exists, err := f.Exists(table, chain, spec...)

View File

@@ -64,6 +64,7 @@ func GetProtocol(ip net.IP) Protocol {
// Client represents any type that can administer iptables rules.
type Client interface {
AppendUnique(table string, chain string, rule ...string) error
Insert(table string, chain string, pos int, rule ...string) error
Delete(table string, chain string, rule ...string) error
Exists(table string, chain string, rule ...string) (bool, error)
List(table string, chain string) ([]string, error)
@@ -75,7 +76,8 @@ type Client interface {
// Rule is an interface for interacting with iptables objects.
type Rule interface {
Add(Client) error
Append(Client) error
Prepend(Client) error
Delete(Client) error
Exists(Client) (bool, error)
String() string
@@ -106,7 +108,14 @@ func NewIPv6Rule(table, chain string, spec ...string) Rule {
return &rule{table, chain, spec, ProtocolIPv6}
}
func (r *rule) Add(client Client) error {
func (r *rule) Prepend(client Client) error {
if err := client.Insert(r.table, r.chain, 1, r.spec...); err != nil {
return fmt.Errorf("failed to add iptables rule: %v", err)
}
return nil
}
func (r *rule) Append(client Client) error {
if err := client.AppendUnique(r.table, r.chain, r.spec...); err != nil {
return fmt.Errorf("failed to add iptables rule: %v", err)
}
@@ -162,7 +171,11 @@ func NewIPv6Chain(table, name string) Rule {
return &chain{table, name, ProtocolIPv6}
}
func (c *chain) Add(client Client) error {
func (c *chain) Prepend(client Client) error {
return c.Append(client)
}
func (c *chain) Append(client Client) error {
// Note: `ClearChain` creates a chain if it does not exist.
if err := client.ClearChain(c.table, c.chain); err != nil {
return fmt.Errorf("failed to add iptables chain: %v", err)
@@ -224,8 +237,9 @@ type Controller struct {
registerer prometheus.Registerer
sync.Mutex
rules []Rule
subscribed bool
appendRules []Rule
prependRules []Rule
subscribed bool
}
// ControllerOption modifies the controller's configuration.
@@ -333,14 +347,14 @@ func (c *Controller) reconcile() error {
c.Lock()
defer c.Unlock()
var rc ruleCache
for i, r := range c.rules {
for i, r := range c.appendRules {
ok, err := rc.exists(c.client(r.Proto()), r)
if err != nil {
return fmt.Errorf("failed to check if rule exists: %v", err)
}
if !ok {
level.Info(c.logger).Log("msg", fmt.Sprintf("applying %d iptables rules", len(c.rules)-i))
if err := c.resetFromIndex(i, c.rules); err != nil {
level.Info(c.logger).Log("msg", fmt.Sprintf("applying %d iptables rules", len(c.appendRules)-i))
if err := c.resetFromIndex(i, c.appendRules); err != nil {
return fmt.Errorf("failed to add rule: %v", err)
}
break
@@ -358,7 +372,7 @@ func (c *Controller) resetFromIndex(i int, rules []Rule) error {
if err := rules[j].Delete(c.client(rules[j].Proto())); err != nil {
return fmt.Errorf("failed to delete rule: %v", err)
}
if err := rules[j].Add(c.client(rules[j].Proto())); err != nil {
if err := rules[j].Append(c.client(rules[j].Proto())); err != nil {
return fmt.Errorf("failed to add rule: %v", err)
}
}
@@ -383,34 +397,87 @@ func (c *Controller) deleteFromIndex(i int, rules *[]Rule) error {
// Set idempotently overwrites any iptables rules previously defined
// for the controller with the given set of rules.
func (c *Controller) Set(rules []Rule) error {
func (c *Controller) Set(rules RuleSet) error {
c.Lock()
defer c.Unlock()
if err := c.setAppendRules(rules.AppendRules); err != nil {
return err
}
return c.setPrependRules(rules.PrependRules)
}
func (c *Controller) setAppendRules(appendRules []Rule) error {
var i int
for ; i < len(rules); i++ {
if i < len(c.rules) {
if rules[i].String() != c.rules[i].String() {
if err := c.deleteFromIndex(i, &c.rules); err != nil {
for ; i < len(appendRules); i++ {
if i < len(c.appendRules) {
if appendRules[i].String() != c.appendRules[i].String() {
if err := c.deleteFromIndex(i, &c.appendRules); err != nil {
return err
}
}
}
if i >= len(c.rules) {
if err := rules[i].Add(c.client(rules[i].Proto())); err != nil {
if i >= len(c.appendRules) {
if err := appendRules[i].Append(c.client(appendRules[i].Proto())); err != nil {
return fmt.Errorf("failed to add rule: %v", err)
}
c.rules = append(c.rules, rules[i])
c.appendRules = append(c.appendRules, appendRules[i])
}
}
return c.deleteFromIndex(i, &c.rules)
err := c.deleteFromIndex(i, &c.appendRules)
if err != nil {
return fmt.Errorf("failed to delete rule: %v", err)
}
return nil
}
func (c *Controller) setPrependRules(prependRules []Rule) error {
for _, prependRule := range prependRules {
if !containsRule(c.prependRules, prependRule) {
if err := prependRule.Prepend(c.client(prependRule.Proto())); err != nil {
return fmt.Errorf("failed to add rule: %v", err)
}
c.prependRules = append(c.prependRules, prependRule)
}
}
for _, existingRule := range c.prependRules {
if !containsRule(prependRules, existingRule) {
if err := existingRule.Delete(c.client(existingRule.Proto())); err != nil {
return fmt.Errorf("failed to delete rule: %v", err)
}
c.prependRules = removeRule(c.prependRules, existingRule)
}
}
return nil
}
func removeRule(rules []Rule, toRemove Rule) []Rule {
ret := make([]Rule, 0, len(rules))
for _, rule := range rules {
if rule.String() != toRemove.String() {
ret = append(ret, rule)
}
}
return ret
}
func containsRule(haystack []Rule, needle Rule) bool {
for _, element := range haystack {
if element.String() == needle.String() {
return true
}
}
return false
}
// CleanUp will clean up any rules created by the controller.
func (c *Controller) CleanUp() error {
c.Lock()
defer c.Unlock()
return c.deleteFromIndex(0, &c.rules)
err := c.deleteFromIndex(0, &c.prependRules)
if err != nil {
return err
}
return c.deleteFromIndex(0, &c.appendRules)
}
func (c *Controller) client(p Protocol) Client {
@@ -430,3 +497,8 @@ func nonBlockingSend(errors chan<- error, err error) {
default:
}
}
type RuleSet struct {
AppendRules []Rule // Rules to append to the chain - order matters.
PrependRules []Rule // Rules to prepend to the chain - order does not matter.
}