-
Notifications
You must be signed in to change notification settings - Fork 5.8k
feat(inputs.nftables): Add plugin #17604
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 11 commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
2cb9dd5
add nftables input plugin
ajohnson-aurora 4e330fd
Add Sudo and Binary options
ansoni d8405dc
Remove unused configuration example
ansoni 8a9e9da
Update Readme with linter feedback
ajohnson-aurora e4b33a2
go fmt updates
ajohnson-aurora 9775fe8
PR Feedback
ajohnson-aurora 5fc2700
missed long line
ajohnson-aurora edd1bd5
Add notlinux compliation and Address some README lint issues
ajohnson-aurora 841b7e4
fix `make check` errors
ajohnson-aurora 0921ca1
PR Feedback
ajohnson-aurora a55e09d
Add back speculative Telegraf Version
ajohnson-aurora 0fef89a
More PR Feedback
ajohnson-aurora 5651dfc
PR Feedback
ajohnson-aurora a46b125
Linter fixes
ajohnson-aurora 34fd401
fix macos linter
ajohnson-aurora 8dd3b49
PR Feedback
ajohnson-aurora 56a509e
Use implicit declaration of error
srebhan a318d13
Improve comment
srebhan 5f42e15
Cleanup init
srebhan ed20c94
Cleanup formatting in sample.conf
srebhan 7fbca44
Cleanup variable naming and parameter order
srebhan f66f82e
Translate unit-test into testcase-based tests
srebhan 15740e4
Remove debug output
srebhan 41926d7
Merge functions
srebhan 24ef6d9
Improve error reporting
srebhan 4314b90
Add test-case for non-existant table
srebhan 0da365f
Update plugins/inputs/nftables/README.md
skartikey e50e2a1
Update plugins/inputs/nftables/README.md
skartikey 2095cbf
fix(inputs.nftables): Fix typo in test case directory name
skartikey File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| //go:build !custom || inputs || inputs.nftables | ||
|
|
||
| package all | ||
|
|
||
| import _ "github.com/influxdata/telegraf/plugins/inputs/nftables" // register plugin |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| # Nftables Plugin | ||
|
|
||
| The nftables plugin gathers packets and bytes counters for rules within | ||
| Linux's nftables firewall. | ||
|
|
||
| Rules are identified through associated comment. | ||
| **Rules without comment are ignored**. | ||
|
|
||
| Before using this plugin **you must ensure that the rules you want to monitor | ||
| are named with a unique comment**. Comments are added using the 'comment | ||
| "my comment"' nftables options. | ||
|
srebhan marked this conversation as resolved.
Outdated
|
||
|
|
||
| The nftables command requires CAP_NET_ADMIN and CAP_NET_RAW capabilities. | ||
| You have several options to grant telegraf to run nftables: | ||
|
srebhan marked this conversation as resolved.
Outdated
|
||
|
|
||
| * Run telegraf as root. This is strongly discouraged. | ||
| * Configure systemd to run telegraf with CAP_NET_ADMIN and CAP_NET_RAW. | ||
| This is the simplest and recommended option. | ||
| * Configure sudo to grant telegraf to run nftables. This is the most restrictive | ||
| option, but requires sudo setup. | ||
|
srebhan marked this conversation as resolved.
Outdated
|
||
|
|
||
| ⭐ Telegraf v1.37.0 | ||
| 🏷️ network, system | ||
|
srebhan marked this conversation as resolved.
Outdated
|
||
| 💻 linux | ||
|
|
||
| ## Using systemd capabilities | ||
|
|
||
| You may run `systemctl edit telegraf.service` and add the following: | ||
|
|
||
| ```text | ||
| [Service] | ||
| CapabilityBoundingSet=CAP_NET_RAW CAP_NET_ADMIN | ||
| AmbientCapabilities=CAP_NET_RAW CAP_NET_ADMIN | ||
| ``` | ||
|
|
||
| Since telegraf will fork a process to run nftables, `AmbientCapabilities` is | ||
| required to transmit the capabilities bounding set to the forked process. | ||
|
|
||
| ## Using sudo | ||
|
|
||
| You may edit your sudo configuration with the following: | ||
|
|
||
| ```sudo | ||
| telegraf ALL=(root) NOPASSWD: /usr/bin/nft * | ||
| ``` | ||
|
srebhan marked this conversation as resolved.
Outdated
|
||
|
|
||
| ## Global configuration options <!-- @/docs/includes/plugin_config.md --> | ||
|
|
||
| In addition to the plugin-specific configuration settings, plugins support | ||
| additional global and plugin configuration settings. These settings are used to | ||
| modify metrics, tags, and field or create aliases and configure ordering, etc. | ||
| See the [CONFIGURATION.md][CONFIGURATION.md] for more details. | ||
|
|
||
| [CONFIGURATION.md]: ../../../docs/CONFIGURATION.md#plugins | ||
|
srebhan marked this conversation as resolved.
Outdated
|
||
|
|
||
| ## Configuration | ||
|
|
||
| ```toml | ||
|
srebhan marked this conversation as resolved.
Outdated
|
||
| # use sudo to run nftables | ||
| use_sudo = false | ||
| # defines the table to monitor: | ||
| tables = ["filter"] | ||
| ``` | ||
|
|
||
| ## Tags | ||
|
|
||
| * All measurements have the following tags: | ||
| * table | ||
| * chain | ||
| * ruleid | ||
|
|
||
| The `ruleid` is the comment associated to the rule. | ||
|
|
||
| ## Metrics | ||
|
|
||
| * nftables | ||
| * pkts (integer, count) | ||
| * bytes (integer, bytes) | ||
|
srebhan marked this conversation as resolved.
Outdated
|
||
|
|
||
| ## Example Output | ||
|
|
||
| ```bash | ||
| $ ./telegraf --config telegraf.conf --input-filter nftables --test | ||
| > nftables,chain=incoming,host=my_hostname,ruleid=comment_val_1,table=filter bytes=66435845i,pkts=133882i 1757367516000000000 | ||
| > nftables,chain=outgoing,host=my_hostname,ruleid=comment_val2,table=filter bytes=25596512i,pkts=145129i 1757367516000000000 | ||
|
skartikey marked this conversation as resolved.
Outdated
|
||
| ``` | ||
|
srebhan marked this conversation as resolved.
Outdated
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,188 @@ | ||
| //go:generate ../../../tools/readme_config_includer/generator | ||
| //go:build linux | ||
|
|
||
| package nftables | ||
|
|
||
| import ( | ||
| _ "embed" | ||
| "encoding/json" | ||
| "errors" | ||
| "fmt" | ||
| "os/exec" | ||
|
|
||
| "github.com/influxdata/telegraf" | ||
| "github.com/influxdata/telegraf/plugins/inputs" | ||
| ) | ||
|
|
||
| const measurement = "nftables" | ||
|
srebhan marked this conversation as resolved.
Outdated
|
||
|
|
||
| type Nftables struct { | ||
| UseSudo bool `toml:"use_sudo"` | ||
| Binary string `toml:"binary"` | ||
|
srebhan marked this conversation as resolved.
Outdated
|
||
| Tables []string | ||
|
skartikey marked this conversation as resolved.
Outdated
|
||
| } | ||
|
|
||
| //go:embed sample.conf | ||
| var sampleConfig string | ||
|
srebhan marked this conversation as resolved.
Outdated
|
||
|
|
||
| type Nftable struct { | ||
| Metainfo *Metainfo | ||
| Rules []*Rule | ||
| JSONSchemaVersion int `json:"json_schema_version"` | ||
|
srebhan marked this conversation as resolved.
Outdated
|
||
| } | ||
|
srebhan marked this conversation as resolved.
Outdated
|
||
|
|
||
| // UnmarshalJSON handles custom parsing of the nftable output which is | ||
| // designed in a generic that is not compatible with the generic parser. | ||
|
srebhan marked this conversation as resolved.
Outdated
|
||
| func (nftable *Nftable) UnmarshalJSON(b []byte) error { | ||
| var atable map[string][]map[string]json.RawMessage | ||
|
skartikey marked this conversation as resolved.
Outdated
|
||
| if err := json.Unmarshal(b, &atable); err != nil { | ||
| return fmt.Errorf("unable to unmarshal: %s", b) | ||
| } | ||
| // []map[string]interface | ||
| nfthings := atable["nftables"] | ||
| for _, nfthing := range nfthings { | ||
| hasKey := func(key string) bool { _, ok := nfthing[key]; return ok } | ||
| switch { | ||
| case hasKey("metainfo"): | ||
| var mi Metainfo | ||
| err := json.Unmarshal(nfthing["metainfo"], &mi) | ||
| if err != nil { | ||
| return fmt.Errorf("unable to parse metadata: %w", err) | ||
| } | ||
| nftable.Metainfo = &mi | ||
| case hasKey("rule"): | ||
| var r Rule | ||
| err := json.Unmarshal(nfthing["rule"], &r) | ||
| if err != nil { | ||
| return fmt.Errorf("unable to parse rule: %w", err) | ||
| } | ||
| nftable.Rules = append(nftable.Rules, &r) | ||
| default: | ||
| // something we are not parsing | ||
| } | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| type Metainfo struct { | ||
| Version string `json:"version"` | ||
| ReleaseName string `json:"release_name"` | ||
| JSONSchemaVersion int `json:"json_schema_version"` | ||
| } | ||
|
|
||
| type Rule struct { | ||
|
skartikey marked this conversation as resolved.
Outdated
|
||
| Family string | ||
| Table string | ||
| Chain string | ||
| Comment string | ||
| Counter *Counter | ||
| } | ||
|
|
||
| // UnmarshalJSON handles properly extracting the counter expression from | ||
| // the Exprs array input | ||
| func (rule *Rule) UnmarshalJSON(b []byte) error { | ||
| var raw struct { | ||
| Family string `json:"family"` | ||
| Table string `json:"table"` | ||
| Chain string `json:"chain"` | ||
| Comment string `json:"comment"` | ||
| Exprs []map[string]json.RawMessage `json:"expr"` | ||
| } | ||
| if err := json.Unmarshal(b, &raw); err != nil { | ||
| return fmt.Errorf("unable to unmarshal: %s", b) | ||
| } | ||
| rule.Family = raw.Family | ||
| rule.Table = raw.Table | ||
| rule.Chain = raw.Chain | ||
| rule.Comment = raw.Comment | ||
|
|
||
| // iterate rule expressions looking for the single counter | ||
| for _, expr := range raw.Exprs { | ||
| hasKey := func(key string) bool { _, ok := expr[key]; return ok } | ||
| if hasKey("counter") { | ||
| rule.Counter = &Counter{} | ||
| if err := json.Unmarshal(expr["counter"], rule.Counter); err != nil { | ||
| return fmt.Errorf("unable to parse counter: %w", err) | ||
| } | ||
| // we can return early since we are not looking for anything else | ||
|
skartikey marked this conversation as resolved.
Outdated
|
||
| return nil | ||
| } | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| type Counter struct { | ||
| Packets int64 `json:"packets"` | ||
| Bytes int64 `json:"bytes"` | ||
| } | ||
|
|
||
| func (*Nftables) SampleConfig() string { | ||
| return sampleConfig | ||
| } | ||
|
|
||
| func (*Nftables) Init() error { | ||
| return nil | ||
| } | ||
|
|
||
| func (nft *Nftables) Gather(acc telegraf.Accumulator) error { | ||
| if len(nft.Tables) == 0 { | ||
| return errors.New("invalid configuration - expected a 'Tables' entry with list of nftables to monitor") | ||
| } | ||
|
srebhan marked this conversation as resolved.
Outdated
|
||
| for _, table := range nft.Tables { | ||
| err := nft.getTableData(table, acc) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
srebhan marked this conversation as resolved.
Outdated
|
||
| } | ||
| return nil | ||
| } | ||
|
|
||
| // List a specific table and add to Accumulator | ||
| func (nft *Nftables) getTableData(tableName string, acc telegraf.Accumulator) error { | ||
|
srebhan marked this conversation as resolved.
Outdated
srebhan marked this conversation as resolved.
Outdated
|
||
| var binary string | ||
| if nft.Binary != "" { | ||
| binary = nft.Binary | ||
| } else { | ||
| binary = "nft" | ||
| } | ||
|
srebhan marked this conversation as resolved.
Outdated
|
||
| nftablePath, err := exec.LookPath(binary) | ||
| if err != nil { | ||
| return errors.New("failed to find nft command ") | ||
|
srebhan marked this conversation as resolved.
Outdated
|
||
| } | ||
|
srebhan marked this conversation as resolved.
Outdated
|
||
| var args []string | ||
| name := nftablePath | ||
| if nft.UseSudo { | ||
| name = "sudo" | ||
| args = append(args, nftablePath) | ||
| } | ||
| args = append(args, "--json", "list", "table", tableName) | ||
|
srebhan marked this conversation as resolved.
Outdated
|
||
| c := exec.Command(name, args...) | ||
| out, err := c.Output() | ||
| if err != nil { | ||
| return fmt.Errorf("error executing %s, error: %w", c, err) | ||
|
skartikey marked this conversation as resolved.
Outdated
|
||
| } | ||
| return parseNftableOutput(out, acc) | ||
| } | ||
|
|
||
| func parseNftableOutput(out []byte, acc telegraf.Accumulator) error { | ||
|
srebhan marked this conversation as resolved.
Outdated
|
||
| var nftable Nftable | ||
| err := json.Unmarshal(out, &nftable) | ||
| if err != nil { | ||
| return fmt.Errorf("error parsing: %s, Error: %w", out, err) | ||
| } | ||
|
srebhan marked this conversation as resolved.
Outdated
|
||
| for _, rule := range nftable.Rules { | ||
| // Rule must have a Counter and a Comment | ||
| if rule.Counter != nil && len(rule.Comment) > 0 { | ||
| fields := map[string]interface{}{"bytes": rule.Counter.Bytes, "pkts": rule.Counter.Packets} | ||
| tags := map[string]string{"table": rule.Table, "chain": rule.Chain, "ruleid": rule.Comment} | ||
| acc.AddFields(measurement, fields, tags) | ||
| } | ||
|
srebhan marked this conversation as resolved.
Outdated
|
||
| } | ||
| return nil | ||
| } | ||
|
|
||
| func init() { | ||
| inputs.Add("nftables", func() telegraf.Input { | ||
| return &Nftables{} | ||
| }) | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| //go:generate ../../../tools/readme_config_includer/generator | ||
| //go:build !linux | ||
|
|
||
| package nftables | ||
|
|
||
| import ( | ||
| _ "embed" | ||
|
|
||
| "github.com/influxdata/telegraf" | ||
| "github.com/influxdata/telegraf/plugins/inputs" | ||
| ) | ||
|
|
||
| //go:embed sample.conf | ||
| var sampleConfig string | ||
|
|
||
| type Nftables struct { | ||
| Log telegraf.Logger `toml:"-"` | ||
| } | ||
|
|
||
| func (*Nftables) SampleConfig() string { return sampleConfig } | ||
|
|
||
| func (i *Nftables) Init() error { | ||
| i.Log.Warn("Current platform is not supported") | ||
| return nil | ||
| } | ||
|
|
||
| func (*Nftables) Gather(_ telegraf.Accumulator) error { return nil } | ||
|
|
||
| func init() { | ||
| inputs.Add("nftables", func() telegraf.Input { | ||
| return &Nftables{} | ||
| }) | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.