Skip to content

Commit 11ac589

Browse files
QxBytesCopilot
andauthored
feat: update iptables monitor with ipv6 and bpf map reading capabilities (#3948)
* update label * add ip6tables * update readme * update logging * add ability to read bpf map to iptables monitor * modify log * display iptables rules block count only when check map enabled * update readme * remove unused function (noop) * address linter * update azure-iptables-monitor/iptables_monitor.go Co-authored-by: Copilot <[email protected]> Signed-off-by: Alexander <[email protected]> * adjust event description * adjust pinned path --------- Signed-off-by: Alexander <[email protected]> Co-authored-by: Copilot <[email protected]>
1 parent d1a7d21 commit 11ac589

File tree

6 files changed

+93
-14
lines changed

6 files changed

+93
-14
lines changed

azure-iptables-monitor/README.md

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,20 @@ Follow the steps below to build and run the program:
2525

2626
4. Start the program with:
2727
```bash
28-
./azure-iptables-monitor --input=/etc/config/ --interval=300
28+
./azure-iptables-monitor -input=/etc/config/ -interval=300
2929
```
30-
- The `--input` flag specifies the directory containing allowed regex pattern files. Default: `/etc/config/`
31-
- The `--interval` flag specifies how often to check iptables rules in seconds. Default: `300`
32-
- The `--events` flag enables Kubernetes event creation for rule violations. Default: `false`
30+
- The `-input` flag specifies the directory containing allowed regex pattern files. Default: `/etc/config/`
31+
- The `-input6` flag specifies the directory containing allowed regex pattern files for IPv6 ip6tables. Default: `/etc/config6/`
32+
- The `-interval` flag specifies how often to check iptables rules and the bpf map in seconds. Default: `300`
33+
- The `-events` flag enables Kubernetes event creation for rule violations. Default: `false`
34+
- The `-ipv6` flag enables IPv6 ip6tables monitoring using the IPv6 allowlists. Default: `false`
35+
- The `-checkMap` flag enables checking the pinned bpf map specified in mapPath for increases. Default: `false`
36+
- The `-mapPath` flag specifies the pinned bpf map path to check. Default: `/azure-block-iptables/iptables_block_event_counter`
3337
- The program must be in a k8s environment and `NODE_NAME` must be a set environment variable with the current node.
3438

35-
5. The program will set the `user-iptables-rules` label to `true` on the specified ciliumnode resource if unexpected rules are found, or `false` if all rules match expected patterns. Proper RBAC is required for patching (patch for ciliumnodes, create for events, get for nodes).
39+
5. The program will set the `kubernetes.azure.com/user-iptables-rules` label to `true` on the specified ciliumnode resource if unexpected rules are found, or `false` if all rules match expected patterns. Proper RBAC is required for patching (patch for ciliumnodes, create for events, get for nodes).
40+
41+
6. The program will also send out an event if the bpf map value specified increases between checks
3642

3743

3844
## Pattern File Format
@@ -48,6 +54,7 @@ Each pattern file should contain one regex pattern per line:
4854
- `nat`, `mangle`, `filter`, `raw`, `security`: Patterns specific to each iptables table
4955
- Empty lines are ignored
5056
- Each line should be a valid Go regex pattern
57+
- The ipv6 config directory uses files with same names, but will match against ipv6 iptables rules
5158

5259
## Debugging
5360

azure-iptables-monitor/go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ require (
1616
github.com/beorn7/perks v1.0.1 // indirect
1717
github.com/blang/semver/v4 v4.0.0 // indirect
1818
github.com/cespare/xxhash/v2 v2.3.0 // indirect
19+
github.com/cilium/ebpf v0.19.0
1920
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
2021
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
2122
github.com/fxamacker/cbor/v2 v2.7.0 // indirect

azure-iptables-monitor/go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM
44
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
55
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
66
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
7+
github.com/cilium/ebpf v0.19.0 h1:Ro/rE64RmFBeA9FGjcTc+KmCeY6jXmryu6FfnzPRIao=
8+
github.com/cilium/ebpf v0.19.0/go.mod h1:fLCgMo3l8tZmAdM3B2XqdFzXBpwkcSTroaVqN08OWVY=
79
github.com/coreos/go-iptables v0.8.0 h1:MPc2P89IhuVpLI7ETL/2tx3XZ61VeICZjYqDEgNsPRc=
810
github.com/coreos/go-iptables v0.8.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
911
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=

azure-iptables-monitor/iptables_monitor.go

Lines changed: 76 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"regexp"
1111
"time"
1212

13+
"github.com/cilium/ebpf"
1314
goiptables "github.com/coreos/go-iptables/iptables"
1415
corev1 "k8s.io/api/core/v1"
1516
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -27,12 +28,16 @@ import (
2728
var version string
2829

2930
var (
30-
configPath = flag.String("input", "/etc/config/", "Name of the directory with the allowed regex files")
31-
checkInterval = flag.Int("interval", 300, "How often to check iptables rules (in seconds)")
31+
configPath4 = flag.String("input", "/etc/config/", "Name of the directory with the ipv4 allowed regex files")
32+
configPath6 = flag.String("input6", "/etc/config6/", "Name of directory with the ipv6 allowed regex files")
33+
checkInterval = flag.Int("interval", 300, "How often to check for user iptables rules and bpf map increases (in seconds)")
3234
sendEvents = flag.Bool("events", false, "Whether to send node events if unexpected iptables rules are detected")
35+
ipv6Enabled = flag.Bool("ipv6", false, "Whether to check ip6tables using the ipv6 allowlists")
36+
checkMap = flag.Bool("checkMap", false, "Whether to check the bpf map at mapPath for increases")
37+
pinPath = flag.String("mapPath", "/azure-block-iptables/iptables_block_event_counter", "Path to pinned bpf map")
3338
)
3439

35-
const label = "user-iptables-rules"
40+
const label = "kubernetes.azure.com/user-iptables-rules"
3641

3742
type FileLineReader interface {
3843
Read(filename string) ([]string, error)
@@ -197,17 +202,19 @@ func hasUnexpectedRules(currentRules, allowedPatterns []string) bool {
197202
// nodeHasUserIPTablesRules returns true if the node has iptables rules that do not match the regex
198203
// specified in the rule's respective table: nat, mangle, filter, raw, or security
199204
// The global file's regexes can match to a rule in any table
200-
func nodeHasUserIPTablesRules(fileReader FileLineReader, iptablesClient IPTablesClient) bool {
205+
func nodeHasUserIPTablesRules(fileReader FileLineReader, path string, iptablesClient IPTablesClient) bool {
201206
tables := []string{"nat", "mangle", "filter", "raw", "security"}
202207

203-
globalPatterns, err := fileReader.Read(filepath.Join(*configPath, "global"))
208+
globalPatterns, err := fileReader.Read(filepath.Join(path, "global"))
204209
if err != nil {
205210
globalPatterns = []string{}
206211
klog.V(2).Infof("No global patterns file found, using empty patterns")
207212
}
208213

209214
userIPTablesRules := false
210215

216+
klog.V(2).Infof("Using reference patterns files in %s", path)
217+
211218
for _, table := range tables {
212219
rules, err := GetRules(iptablesClient, table)
213220
if err != nil {
@@ -216,7 +223,7 @@ func nodeHasUserIPTablesRules(fileReader FileLineReader, iptablesClient IPTables
216223
}
217224

218225
var referencePatterns []string
219-
referencePatterns, err = fileReader.Read(filepath.Join(*configPath, table))
226+
referencePatterns, err = fileReader.Read(filepath.Join(path, table))
220227
if err != nil {
221228
referencePatterns = []string{}
222229
klog.V(2).Infof("No reference patterns file found for table %s", table)
@@ -234,6 +241,23 @@ func nodeHasUserIPTablesRules(fileReader FileLineReader, iptablesClient IPTables
234241
return userIPTablesRules
235242
}
236243

244+
func getBPFMapValue() (uint64, error) {
245+
m, err := ebpf.LoadPinnedMap(*pinPath, nil)
246+
if err != nil {
247+
return 0, fmt.Errorf("failed to load pinned map %s: %w", *pinPath, err)
248+
}
249+
defer m.Close()
250+
// 0 is the key for # of blocks
251+
key := uint32(0)
252+
value := uint64(0)
253+
254+
if err := m.Lookup(&key, &value); err != nil {
255+
return 0, fmt.Errorf("failed to lookup key %d in bpf map: %w", key, err)
256+
}
257+
258+
return value, nil
259+
}
260+
237261
func main() {
238262
klog.InitFlags(nil)
239263
flag.Parse()
@@ -263,6 +287,15 @@ func main() {
263287
klog.Fatalf("failed to create iptables client: %v", err)
264288
}
265289

290+
var ip6tablesClient IPTablesClient
291+
if *ipv6Enabled {
292+
ip6tablesClient, err = goiptables.New(goiptables.IPFamily(goiptables.ProtocolIPv6))
293+
if err != nil {
294+
klog.Fatalf("failed to create ip6tables client: %v", err)
295+
}
296+
}
297+
klog.Infof("IPv6: %v", *ipv6Enabled)
298+
266299
// get current node name from environment variable
267300
currentNodeName := os.Getenv("NODE_NAME")
268301
if currentNodeName == "" {
@@ -273,8 +306,22 @@ func main() {
273306

274307
var fileReader FileLineReader = OSFileLineReader{}
275308

309+
previousBlocks := uint64(0)
310+
276311
for {
277-
userIPTablesRulesFound := nodeHasUserIPTablesRules(fileReader, iptablesClient)
312+
userIPTablesRulesFound := nodeHasUserIPTablesRules(fileReader, *configPath4, iptablesClient)
313+
if userIPTablesRulesFound {
314+
klog.Info("Above user iptables rules detected in IPv4 iptables")
315+
}
316+
317+
// check ip6tables rules if enabled
318+
if *ipv6Enabled {
319+
userIP6TablesRulesFound := nodeHasUserIPTablesRules(fileReader, *configPath6, ip6tablesClient)
320+
if userIP6TablesRulesFound {
321+
klog.Info("Above user iptables rules detected in IPv6 iptables")
322+
}
323+
userIPTablesRulesFound = userIPTablesRulesFound || userIP6TablesRulesFound
324+
}
278325

279326
// update label based on whether user iptables rules were found
280327
err = patchLabel(dynamicClient, userIPTablesRulesFound, currentNodeName)
@@ -291,6 +338,28 @@ func main() {
291338
}
292339
}
293340

341+
// if disabled the number of blocks never increases from zero
342+
currentBlocks := uint64(0)
343+
if *checkMap {
344+
// read bpf map to check for number of blocked iptables rules
345+
currentBlocks, err = getBPFMapValue()
346+
if err != nil {
347+
klog.Errorf("failed to get bpf map value: %v", err)
348+
}
349+
klog.V(2).Infof("IPTables rules blocks: Previous: %d Current: %d", previousBlocks, currentBlocks)
350+
}
351+
// if number of blocked rules increased since last time
352+
blockedRulesIncreased := currentBlocks > previousBlocks
353+
if *sendEvents && blockedRulesIncreased {
354+
msg := "A process attempted to add iptables rules to the node but was blocked since last check. " +
355+
"iptables rules blocked because EBPF Host Routing is enabled: aka.ms/acnsperformance"
356+
err = createNodeEvent(clientset, currentNodeName, "BlockedIPTablesRule", msg, corev1.EventTypeWarning)
357+
if err != nil {
358+
klog.Errorf("failed to create iptables block event: %v", err)
359+
}
360+
}
361+
previousBlocks = currentBlocks
362+
294363
time.Sleep(time.Duration(*checkInterval) * time.Second)
295364
}
296365
}

azure-iptables-monitor/iptables_monitor_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ func TestNodeHasUserIPTablesRules(t *testing.T) {
217217
fileReader.files = tc.files
218218
iptablesClient.rules = tc.rules
219219

220-
result := nodeHasUserIPTablesRules(fileReader, iptablesClient)
220+
result := nodeHasUserIPTablesRules(fileReader, "/etc/config/", iptablesClient)
221221
require.Equal(t, tc.expected, result, tc.description)
222222
})
223223
}

bpf-prog/azure-block-iptables/pkg/bpfprogram/program.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import (
1818

1919
const (
2020
// BPFMapPinPath is the directory where BPF maps are pinned
21-
BPFMapPinPath = "/sys/fs/bpf/block-iptables"
21+
BPFMapPinPath = "/sys/fs/bpf/azure-block-iptables"
2222
// EventCounterMapName is the name used for pinning the event counter map
2323
EventCounterMapName = "iptables_block_event_counter"
2424
// IptablesLegacyBlockProgramName is the name used for pinning the legacy iptables block program

0 commit comments

Comments
 (0)