From 3d600728b20493706dc56345cd80c45cf681fe7b Mon Sep 17 00:00:00 2001 From: Tiago Queiroz Date: Thu, 16 May 2024 12:53:13 -0400 Subject: [PATCH 01/14] Only start Journald input with supported systemd versions --- Vagrantfile | 2 + filebeat/docs/inputs/input-journald.asciidoc | 3 + filebeat/input/journald/environment_test.go | 2 +- filebeat/input/journald/input.go | 67 ++++++++++++++++--- filebeat/input/journald/input_test.go | 50 ++++++++++++++ libbeat/common/seccomp/policy_linux_386.go | 1 + libbeat/common/seccomp/policy_linux_amd64.go | 1 + libbeat/common/seccomp/policy_linux_arm64.go | 1 + .../seccomp/seccomp-profiler-blacklist.txt | 1 - 9 files changed, 117 insertions(+), 11 deletions(-) diff --git a/Vagrantfile b/Vagrantfile index 47bc686d74f..056cd39d774 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -47,6 +47,8 @@ TEST_BOXES = [ {:name => "ubuntu1604", :box => "ubuntu/xenial64", :platform => "ubuntu"}, {:name => "ubuntu1804", :box => "ubuntu/bionic64", :platform => "ubuntu"}, {:name => "ubuntu2004", :box => "ubuntu/focal64", :platform => "ubuntu"}, + {:name => "ubuntu2204", :box => "ubuntu/jammy64", :platform => "ubuntu"}, + {:name => "ubuntu2404", :box => "ubuntu/noble64", :platform => "ubuntu"}, {:name => "debian8", :box => "generic/debian8", :platform => "debian"}, {:name => "debian9", :box => "debian/stretch64", :platform => "debian"}, diff --git a/filebeat/docs/inputs/input-journald.asciidoc b/filebeat/docs/inputs/input-journald.asciidoc index 5b932b4d133..39a4aae5515 100644 --- a/filebeat/docs/inputs/input-journald.asciidoc +++ b/filebeat/docs/inputs/input-journald.asciidoc @@ -13,6 +13,9 @@ https://www.freedesktop.org/software/systemd/man/systemd-journald.service.html[` is a system service that collects and stores logging data. The `journald` input reads this log data and the metadata associated with it. +WARNING: There is bug in journald < v255 that causes {beatname_uc} to +crash. Only use this input with journald >= v255. + The simplest configuration example is one that reads all logs from the default journal. diff --git a/filebeat/input/journald/environment_test.go b/filebeat/input/journald/environment_test.go index fdcf201d365..4cf79e3c1e3 100644 --- a/filebeat/input/journald/environment_test.go +++ b/filebeat/input/journald/environment_test.go @@ -72,7 +72,7 @@ func (e *inputTestingEnvironment) mustCreateInput(config map[string]interface{}) e.t.Helper() e.grp = unison.TaskGroup{} manager := e.getManager() - if err := manager.Init(&e.grp, v2.ModeRun); err != nil { + if err := manager.Init(&e.grp); err != nil { e.t.Fatalf("failed to initialise manager: %+v", err) } diff --git a/filebeat/input/journald/input.go b/filebeat/input/journald/input.go index c32d677ffa4..0499b92236d 100644 --- a/filebeat/input/journald/input.go +++ b/filebeat/input/journald/input.go @@ -20,11 +20,14 @@ package journald import ( + "context" + "errors" + "fmt" + "os/exec" + "strings" "time" "github.com/coreos/go-systemd/v22/sdjournal" - "github.com/urso/sderr" - "github.com/elastic/beats/v7/filebeat/input/journald/pkg/journalfield" "github.com/elastic/beats/v7/filebeat/input/journald/pkg/journalread" input "github.com/elastic/beats/v7/filebeat/input/v2" @@ -35,6 +38,7 @@ import ( "github.com/elastic/beats/v7/libbeat/reader/parser" conf "github.com/elastic/elastic-agent-libs/config" "github.com/elastic/elastic-agent-libs/logp" + "github.com/urso/sderr" ) type journald struct { @@ -63,21 +67,44 @@ const localSystemJournalID = "LOCAL_SYSTEM_JOURNAL" const pluginName = "journald" +var ErrSystemdVersionNotSupported = errors.New("systemd version must be >= 255") +var ErrCannotGetSystemdVersion = errors.New("cannot get systemd version") + // Plugin creates a new journald input plugin for creating a stateful input. func Plugin(log *logp.Logger, store cursor.StateStore) input.Plugin { - return input.Plugin{ + m := &cursor.InputManager{ + Logger: log, + StateStore: store, + Type: pluginName, + Configure: configure, + } + p := input.Plugin{ Name: pluginName, Stability: feature.Experimental, Deprecated: false, Info: "journald input", Doc: "The journald input collects logs from the local journald service", - Manager: &cursor.InputManager{ - Logger: log, - StateStore: store, - Type: pluginName, - Configure: configure, - }, + Manager: m, } + + version, err := getJournaldVersion() + if err != nil { + configErr := fmt.Errorf("%w: %s", ErrCannotGetSystemdVersion, err) + m.Configure = func(_ *conf.C) ([]cursor.Source, cursor.Input, error) { + return nil, nil, configErr + } + return p + } + + if version < 255 { + configErr := fmt.Errorf("%w. Systemd version: %d", ErrSystemdVersionNotSupported, version) + m.Configure = func(_ *conf.C) ([]cursor.Source, cursor.Input, error) { + return nil, nil, configErr + } + return p + } + + return p } type pathSource string @@ -303,3 +330,25 @@ func (r *readerAdapter) Next() (reader.Message, error) { return m, nil } +func getJournaldVersion() (int, error) { + cmd := exec.CommandContext(context.Background(), "journalctl", "--version") + outputBytes, err := cmd.CombinedOutput() + if err != nil { + return 0, fmt.Errorf("cannot call journald. Err: '%w'. Output: '%s'", err, string(outputBytes)) + } + + return parseJournaldVersion(string(outputBytes)) +} + +func parseJournaldVersion(output string) (int, error) { + lines := strings.Split(output, "\n") + if len(lines) < 2 { + return 0, fmt.Errorf("unexpected format for version command. Returned value: '%s'", output) + } + + versionLine := lines[0] + version := 0 + _, err := fmt.Sscanf(versionLine, "systemd %d", &version) + + return version, err +} diff --git a/filebeat/input/journald/input_test.go b/filebeat/input/journald/input_test.go index f42bac174fb..f670224c598 100644 --- a/filebeat/input/journald/input_test.go +++ b/filebeat/input/journald/input_test.go @@ -84,3 +84,53 @@ func TestInputFieldsTranslation(t *testing.T) { }) } } + +func TestParseJournaldVersion(t *testing.T) { + foo := map[string]struct { + data string + expected int + }{ + "Archlinux": { + expected: 255, + data: `systemd 255 (255.6-1-arch) ++PAM +AUDIT -SELINUX -APPARMOR -IMA +SMACK +SECCOMP +GCRYPT +GNUTLS +OPENSSL +ACL +BLKID +CURL +ELFUTILS +FIDO2 +IDN2 -IDN +IPTC +KMOD +LIBCRYPTSETUP +LIBFDISK +PCRE2 +PWQUALITY +P11KIT +QRENCODE +TPM2 +BZIP2 +LZ4 +XZ +ZLIB +ZSTD +BPF_FRAMEWORK +XKBCOMMON +UTMP -SYSVINIT default-hierarchy=unified`, + }, + "AmazonLinux2": { + expected: 219, + data: `systemd 219 + +PAM +AUDIT +SELINUX +IMA -APPARMOR +SMACK +SYSVINIT +UTMP +LIBCRYPTSETUP +GCRYPT +GNUTLS +ACL +XZ +LZ4 -SECCOMP +BLKID +ELFUTILS +KMOD +IDN`, + }, + "Ubuntu 2204": { + expected: 249, + data: `systemd 249 (249.11-0ubuntu3.12) + +PAM +AUDIT +SELINUX +APPARMOR +IMA +SMACK +SECCOMP +GCRYPT +GNUTLS +OPENSSL +ACL +BLKID +CURL +ELFUTILS +FIDO2 +IDN2 -IDN +IPTC +KMOD +LIBCRYPTSETUP +LIBFDISK +PCRE2 -PWQUALITY -P11KIT -QRENCODE +BZIP2 +LZ4 +XZ +ZLIB +ZSTD -XKBCOMMON +UTMP +SYSVINIT default-hierarchy=unified`, + }, + } + + for name, tc := range foo { + t.Run(name, func(t *testing.T) { + version, err := parseJournaldVersion(tc.data) + if err != nil { + t.Errorf("did not expect an error: %s", err) + } + + if version != tc.expected { + t.Errorf("expecting version %d, got %d", tc.expected, version) + } + }) + } +} + +func TestGetJounraldVersion(t *testing.T) { + // This test already has build tags to only run on systems + // with systemd. So there should be no problem calling + // journalctl directly. + version, err := getJournaldVersion() + if err != nil { + t.Fatalf("did not expect an error: %s", err) + } + + if version == 0 { + t.Fatal("version must be grater than 0") + } +} diff --git a/libbeat/common/seccomp/policy_linux_386.go b/libbeat/common/seccomp/policy_linux_386.go index ac2a93a5c74..f5cf80f21d0 100644 --- a/libbeat/common/seccomp/policy_linux_386.go +++ b/libbeat/common/seccomp/policy_linux_386.go @@ -57,6 +57,7 @@ func init() { "fcntl64", "fdatasync", "flock", + "fork", "fstat64", "fstatat64", "fsync", diff --git a/libbeat/common/seccomp/policy_linux_amd64.go b/libbeat/common/seccomp/policy_linux_amd64.go index 624f48c890a..b4617031b0e 100644 --- a/libbeat/common/seccomp/policy_linux_amd64.go +++ b/libbeat/common/seccomp/policy_linux_amd64.go @@ -61,6 +61,7 @@ func init() { "fcntl", "fdatasync", "flock", + "fork", "fstat", "fstatfs", "fsync", diff --git a/libbeat/common/seccomp/policy_linux_arm64.go b/libbeat/common/seccomp/policy_linux_arm64.go index bcd6e0ae915..a51310732aa 100644 --- a/libbeat/common/seccomp/policy_linux_arm64.go +++ b/libbeat/common/seccomp/policy_linux_arm64.go @@ -27,6 +27,7 @@ func init() { Action: seccomp.ActionErrno, Names: []string{ "execveat", + "fork", }, }, }, diff --git a/libbeat/common/seccomp/seccomp-profiler-blacklist.txt b/libbeat/common/seccomp/seccomp-profiler-blacklist.txt index 1f5f5aa10f8..b9add962d8d 100644 --- a/libbeat/common/seccomp/seccomp-profiler-blacklist.txt +++ b/libbeat/common/seccomp/seccomp-profiler-blacklist.txt @@ -4,7 +4,6 @@ clock_adjtime create_module delete_module execveat -fork init_module mount prctl From f3c40914fbeb5693c254e2ebde7a753c6ba63d72 Mon Sep 17 00:00:00 2001 From: Tiago Queiroz Date: Thu, 16 May 2024 14:47:12 -0400 Subject: [PATCH 02/14] mage check and fix lint warnings --- filebeat/input/journald/input.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/filebeat/input/journald/input.go b/filebeat/input/journald/input.go index 0499b92236d..f1352c03f5a 100644 --- a/filebeat/input/journald/input.go +++ b/filebeat/input/journald/input.go @@ -28,6 +28,8 @@ import ( "time" "github.com/coreos/go-systemd/v22/sdjournal" + "github.com/urso/sderr" + "github.com/elastic/beats/v7/filebeat/input/journald/pkg/journalfield" "github.com/elastic/beats/v7/filebeat/input/journald/pkg/journalread" input "github.com/elastic/beats/v7/filebeat/input/v2" @@ -38,7 +40,6 @@ import ( "github.com/elastic/beats/v7/libbeat/reader/parser" conf "github.com/elastic/elastic-agent-libs/config" "github.com/elastic/elastic-agent-libs/logp" - "github.com/urso/sderr" ) type journald struct { From 6329e846101af11a8caf75304f93bbed38d18a3a Mon Sep 17 00:00:00 2001 From: Tiago Queiroz Date: Thu, 16 May 2024 15:33:11 -0400 Subject: [PATCH 03/14] Fix Vagrant file and add changelog entry --- CHANGELOG.next.asciidoc | 1 + Vagrantfile | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index a45a61e3e41..545d62e855b 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -289,6 +289,7 @@ https://github.com/elastic/beats/compare/v8.8.1\...main[Check the HEAD diff] - Make HTTP Endpoint input GA. {issue}38979[38979] {pull}39410[39410] - Update CEL mito extensions to v1.12.2. {pull}39755[39755] - Add support for base64-encoded HMAC headers to HTTP Endpoint. {pull}39655[39655] +- Journald input validates the minimum compatible version of Systemd and will fail to start if a Journald input is configured in an incompatible host {issue}34077[34077] {pull}39605[39605] *Auditbeat* diff --git a/Vagrantfile b/Vagrantfile index 056cd39d774..6482271f2f1 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -48,7 +48,6 @@ TEST_BOXES = [ {:name => "ubuntu1804", :box => "ubuntu/bionic64", :platform => "ubuntu"}, {:name => "ubuntu2004", :box => "ubuntu/focal64", :platform => "ubuntu"}, {:name => "ubuntu2204", :box => "ubuntu/jammy64", :platform => "ubuntu"}, - {:name => "ubuntu2404", :box => "ubuntu/noble64", :platform => "ubuntu"}, {:name => "debian8", :box => "generic/debian8", :platform => "debian"}, {:name => "debian9", :box => "debian/stretch64", :platform => "debian"}, From 479334764c7ad2e69fbe18f862213e2023b5a89c Mon Sep 17 00:00:00 2001 From: Tiago Queiroz Date: Thu, 16 May 2024 15:46:57 -0400 Subject: [PATCH 04/14] Add fork to allow system calls list --- libbeat/common/seccomp/seccomp-profiler-allow.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libbeat/common/seccomp/seccomp-profiler-allow.txt b/libbeat/common/seccomp/seccomp-profiler-allow.txt index 34e31cbfe12..afca7f3d753 100644 --- a/libbeat/common/seccomp/seccomp-profiler-allow.txt +++ b/libbeat/common/seccomp/seccomp-profiler-allow.txt @@ -29,3 +29,6 @@ wait4 # Reexec execve + +# Get Systemd version +fork From ff71181bb17bf9c83bea7196956ee6ce4fb73aeb Mon Sep 17 00:00:00 2001 From: Tiago Queiroz Date: Fri, 17 May 2024 12:11:52 -0400 Subject: [PATCH 05/14] Remove the `fork` syscall --- libbeat/common/seccomp/policy_linux_386.go | 1 - libbeat/common/seccomp/policy_linux_amd64.go | 1 - libbeat/common/seccomp/policy_linux_arm64.go | 1 - libbeat/common/seccomp/seccomp-profiler-blacklist.txt | 1 + 4 files changed, 1 insertion(+), 3 deletions(-) diff --git a/libbeat/common/seccomp/policy_linux_386.go b/libbeat/common/seccomp/policy_linux_386.go index f5cf80f21d0..ac2a93a5c74 100644 --- a/libbeat/common/seccomp/policy_linux_386.go +++ b/libbeat/common/seccomp/policy_linux_386.go @@ -57,7 +57,6 @@ func init() { "fcntl64", "fdatasync", "flock", - "fork", "fstat64", "fstatat64", "fsync", diff --git a/libbeat/common/seccomp/policy_linux_amd64.go b/libbeat/common/seccomp/policy_linux_amd64.go index b4617031b0e..624f48c890a 100644 --- a/libbeat/common/seccomp/policy_linux_amd64.go +++ b/libbeat/common/seccomp/policy_linux_amd64.go @@ -61,7 +61,6 @@ func init() { "fcntl", "fdatasync", "flock", - "fork", "fstat", "fstatfs", "fsync", diff --git a/libbeat/common/seccomp/policy_linux_arm64.go b/libbeat/common/seccomp/policy_linux_arm64.go index a51310732aa..bcd6e0ae915 100644 --- a/libbeat/common/seccomp/policy_linux_arm64.go +++ b/libbeat/common/seccomp/policy_linux_arm64.go @@ -27,7 +27,6 @@ func init() { Action: seccomp.ActionErrno, Names: []string{ "execveat", - "fork", }, }, }, diff --git a/libbeat/common/seccomp/seccomp-profiler-blacklist.txt b/libbeat/common/seccomp/seccomp-profiler-blacklist.txt index b9add962d8d..1f5f5aa10f8 100644 --- a/libbeat/common/seccomp/seccomp-profiler-blacklist.txt +++ b/libbeat/common/seccomp/seccomp-profiler-blacklist.txt @@ -4,6 +4,7 @@ clock_adjtime create_module delete_module execveat +fork init_module mount prctl From 47a057cbc04ab4a49daabd6fd2314058496b1351 Mon Sep 17 00:00:00 2001 From: Tiago Queiroz Date: Thu, 23 May 2024 09:53:29 -0400 Subject: [PATCH 06/14] Use D-Bus to get Systemd version --- filebeat/input/journald/input.go | 81 +++++++++++++++++++++------ filebeat/input/journald/input_test.go | 22 +++----- 2 files changed, 73 insertions(+), 30 deletions(-) diff --git a/filebeat/input/journald/input.go b/filebeat/input/journald/input.go index f1352c03f5a..736569c00bf 100644 --- a/filebeat/input/journald/input.go +++ b/filebeat/input/journald/input.go @@ -20,14 +20,14 @@ package journald import ( - "context" "errors" "fmt" - "os/exec" + "strconv" "strings" "time" "github.com/coreos/go-systemd/v22/sdjournal" + "github.com/godbus/dbus/v5" "github.com/urso/sderr" "github.com/elastic/beats/v7/filebeat/input/journald/pkg/journalfield" @@ -63,6 +63,10 @@ type checkpoint struct { MonotonicTimestamp uint64 } +// errCannotConnectToDBus is returned when the connection to D-Bus +// cannot be established. +var errCannotConnectToDBus = errors.New("cannot connect to D-Bus") + // LocalSystemJournalID is the ID of the local system journal. const localSystemJournalID = "LOCAL_SYSTEM_JOURNAL" @@ -88,7 +92,7 @@ func Plugin(log *logp.Logger, store cursor.StateStore) input.Plugin { Manager: m, } - version, err := getJournaldVersion() + version, err := systemdVersion() if err != nil { configErr := fmt.Errorf("%w: %s", ErrCannotGetSystemdVersion, err) m.Configure = func(_ *conf.C) ([]cursor.Source, cursor.Input, error) { @@ -331,25 +335,70 @@ func (r *readerAdapter) Next() (reader.Message, error) { return m, nil } -func getJournaldVersion() (int, error) { - cmd := exec.CommandContext(context.Background(), "journalctl", "--version") - outputBytes, err := cmd.CombinedOutput() + +// parseSystemdVersion parses the string version from Systemd fetched via D-Bus. +// +// The expected format is: +// +// - 255.6-1-arch +// - 252.16-1.amzn2023.0.2 +// +// The function will parse and return the integer before the full stop. +func parseSystemdVersion(output string) (int, error) { + parts := strings.Split(output, ".") + if len(parts) < 2 { + return 0, errors.New("unexpected format for version.") + } + + version, err := strconv.Atoi(parts[0]) + if err != nil { + return 0, fmt.Errorf("cannot parse Systemd version: %s", err) + } + + return version, err +} + +// getSystemdVersionViaDBus foo +// +// Version string should not be parsed: +// +// Version encodes the version string of the running systemd +// instance. Note that the version string is purely informational, +// it should not be parsed, one may not assume the version to be +// formatted in any particular way. We take the liberty to change +// the versioning scheme at any time and it is not part of the API. +// Source: https://www.freedesktop.org/wiki/Software/systemd/dbus/ +func getSystemdVersionViaDBus() (string, error) { + conn, err := dbus.ConnectSessionBus() + if err != nil { + return "", fmt.Errorf("%w: %w", errCannotConnectToDBus, err) + } + defer conn.Close() + + obj := conn.Object("org.freedesktop.systemd1", "/org/freedesktop/systemd1") + resp, err := obj.GetProperty("org.freedesktop.systemd1.Manager.Version") if err != nil { - return 0, fmt.Errorf("cannot call journald. Err: '%w'. Output: '%s'", err, string(outputBytes)) + return "", fmt.Errorf("cannot get version property from D-Bus %w", err) } - return parseJournaldVersion(string(outputBytes)) + version := "" + if err := resp.Store(&version); err != nil { + return "", fmt.Errorf("cannot store Systemd version into Go string: %s", err) + } + + return version, nil } -func parseJournaldVersion(output string) (int, error) { - lines := strings.Split(output, "\n") - if len(lines) < 2 { - return 0, fmt.Errorf("unexpected format for version command. Returned value: '%s'", output) +func systemdVersion() (int, error) { + versionStr, err := getSystemdVersionViaDBus() + if err != nil { + return 0, fmt.Errorf("caanot get Systemd version: %w", err) } - versionLine := lines[0] - version := 0 - _, err := fmt.Sscanf(versionLine, "systemd %d", &version) + version, err := parseSystemdVersion(versionStr) + if err != nil { + return 0, fmt.Errorf("cannot parse Systemd version: %w", err) + } - return version, err + return version, nil } diff --git a/filebeat/input/journald/input_test.go b/filebeat/input/journald/input_test.go index f670224c598..5c81c52998c 100644 --- a/filebeat/input/journald/input_test.go +++ b/filebeat/input/journald/input_test.go @@ -92,24 +92,21 @@ func TestParseJournaldVersion(t *testing.T) { }{ "Archlinux": { expected: 255, - data: `systemd 255 (255.6-1-arch) -+PAM +AUDIT -SELINUX -APPARMOR -IMA +SMACK +SECCOMP +GCRYPT +GNUTLS +OPENSSL +ACL +BLKID +CURL +ELFUTILS +FIDO2 +IDN2 -IDN +IPTC +KMOD +LIBCRYPTSETUP +LIBFDISK +PCRE2 +PWQUALITY +P11KIT +QRENCODE +TPM2 +BZIP2 +LZ4 +XZ +ZLIB +ZSTD +BPF_FRAMEWORK +XKBCOMMON +UTMP -SYSVINIT default-hierarchy=unified`, + data: `255.6-1-arch`, }, "AmazonLinux2": { - expected: 219, - data: `systemd 219 - +PAM +AUDIT +SELINUX +IMA -APPARMOR +SMACK +SYSVINIT +UTMP +LIBCRYPTSETUP +GCRYPT +GNUTLS +ACL +XZ +LZ4 -SECCOMP +BLKID +ELFUTILS +KMOD +IDN`, + expected: 252, + data: `252.16-1.amzn2023.0.2`, }, "Ubuntu 2204": { expected: 249, - data: `systemd 249 (249.11-0ubuntu3.12) - +PAM +AUDIT +SELINUX +APPARMOR +IMA +SMACK +SECCOMP +GCRYPT +GNUTLS +OPENSSL +ACL +BLKID +CURL +ELFUTILS +FIDO2 +IDN2 -IDN +IPTC +KMOD +LIBCRYPTSETUP +LIBFDISK +PCRE2 -PWQUALITY -P11KIT -QRENCODE +BZIP2 +LZ4 +XZ +ZLIB +ZSTD -XKBCOMMON +UTMP +SYSVINIT default-hierarchy=unified`, + data: `249.11-0ubuntu3.12`, }, } for name, tc := range foo { t.Run(name, func(t *testing.T) { - version, err := parseJournaldVersion(tc.data) + version, err := parseSystemdVersion(tc.data) if err != nil { t.Errorf("did not expect an error: %s", err) } @@ -122,15 +119,12 @@ func TestParseJournaldVersion(t *testing.T) { } func TestGetJounraldVersion(t *testing.T) { - // This test already has build tags to only run on systems - // with systemd. So there should be no problem calling - // journalctl directly. - version, err := getJournaldVersion() + version, err := getSystemdVersionViaDBus() if err != nil { t.Fatalf("did not expect an error: %s", err) } - if version == 0 { - t.Fatal("version must be grater than 0") + if version == "" { + t.Fatal("version must not be an empty string") } } From fd998fd215bb28acb74e9a89f97a742c871a1aa1 Mon Sep 17 00:00:00 2001 From: Tiago Queiroz Date: Thu, 23 May 2024 10:52:29 -0400 Subject: [PATCH 07/14] Update changelog --- CHANGELOG.next.asciidoc | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index 545d62e855b..f65202b3128 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -290,6 +290,7 @@ https://github.com/elastic/beats/compare/v8.8.1\...main[Check the HEAD diff] - Update CEL mito extensions to v1.12.2. {pull}39755[39755] - Add support for base64-encoded HMAC headers to HTTP Endpoint. {pull}39655[39655] - Journald input validates the minimum compatible version of Systemd and will fail to start if a Journald input is configured in an incompatible host {issue}34077[34077] {pull}39605[39605] +- Journald input validates the minimum compatible version of Systemd and will fail to start if the Sytemd version in the host is incompatible {issue}34077[34077] {pull}39605[39605] *Auditbeat* From 1dab86d3d24b2866160828ab64a5b602f0197c2a Mon Sep 17 00:00:00 2001 From: Tiago Queiroz Date: Thu, 23 May 2024 10:56:32 -0400 Subject: [PATCH 08/14] remove fork from seccopm allow list --- libbeat/common/seccomp/seccomp-profiler-allow.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/libbeat/common/seccomp/seccomp-profiler-allow.txt b/libbeat/common/seccomp/seccomp-profiler-allow.txt index afca7f3d753..34e31cbfe12 100644 --- a/libbeat/common/seccomp/seccomp-profiler-allow.txt +++ b/libbeat/common/seccomp/seccomp-profiler-allow.txt @@ -29,6 +29,3 @@ wait4 # Reexec execve - -# Get Systemd version -fork From 3a83fa3f8582ed2f228ebc8a2be132bfbae94dc9 Mon Sep 17 00:00:00 2001 From: Tiago Queiroz Date: Thu, 23 May 2024 11:14:44 -0400 Subject: [PATCH 09/14] Add mage target to test journald --- filebeat/magefile.go | 13 +++++++++++++ x-pack/filebeat/magefile.go | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/filebeat/magefile.go b/filebeat/magefile.go index a8defd10562..771741f02a1 100644 --- a/filebeat/magefile.go +++ b/filebeat/magefile.go @@ -214,3 +214,16 @@ func PythonIntegTest(ctx context.Context) error { mg.Deps(Fields, Dashboards, devtools.BuildSystemTestBinary) return devtools.PythonIntegTestFromHost(devtools.DefaultPythonTestIntegrationFromHostArgs()) } + +// TestJournald executes the Journald input tests +// Use TEST_COVERAGE=true to enable code coverage profiling. +// Use RACE_DETECTOR=true to enable the race detector. +func TestJournald(ctx context.Context) error { + utArgs := devtools.DefaultGoTestUnitArgs() + utArgs.Packages = []string{"./input/journald"} + if devtools.Platform.GOOS == "linux" { + utArgs.ExtraFlags = append(utArgs.ExtraFlags, "-tags=withjournald") + } + + return devtools.GoTest(ctx, utArgs) +} diff --git a/x-pack/filebeat/magefile.go b/x-pack/filebeat/magefile.go index aaf1326de35..e5b809f3abf 100644 --- a/x-pack/filebeat/magefile.go +++ b/x-pack/filebeat/magefile.go @@ -187,3 +187,16 @@ func PythonIntegTest(ctx context.Context) error { mg.Deps(Fields, Dashboards, devtools.BuildSystemTestBinary) return devtools.PythonIntegTestFromHost(devtools.DefaultPythonTestIntegrationFromHostArgs()) } + +// TestJournald executes the Journald input tests +// Use TEST_COVERAGE=true to enable code coverage profiling. +// Use RACE_DETECTOR=true to enable the race detector. +func TestJournald(ctx context.Context) error { + utArgs := devtools.DefaultGoTestUnitArgs() + utArgs.Packages = []string{"./input/journald"} + if devtools.Platform.GOOS == "linux" { + utArgs.ExtraFlags = append(utArgs.ExtraFlags, "-tags=withjournald") + } + + return devtools.GoTest(ctx, utArgs) +} From 2111c206fefea5d267d4735c49328d5110ecd026 Mon Sep 17 00:00:00 2001 From: Tiago Queiroz Date: Thu, 23 May 2024 15:53:45 -0400 Subject: [PATCH 10/14] Add flag to ignore Systemd version and improve tests This commit adds a flag to ignore the Systemd version check for the Journald input. The mage targets are updated so the Journald input unit tests always run. The tests disable the Systemd version check so we can run the tests in our current hosts. --- filebeat/cmd/root.go | 1 + filebeat/input/journald/input.go | 24 ++++++++++++++++++++++-- filebeat/input/journald/input_test.go | 8 ++++++++ filebeat/magefile.go | 19 +++++++++++++------ libbeat/docs/command-reference.asciidoc | 7 +++++++ 5 files changed, 51 insertions(+), 8 deletions(-) diff --git a/filebeat/cmd/root.go b/filebeat/cmd/root.go index 4a5a2607b18..81d87807fcf 100644 --- a/filebeat/cmd/root.go +++ b/filebeat/cmd/root.go @@ -63,6 +63,7 @@ func Filebeat(inputs beater.PluginFactory, settings instance.Settings) *cmd.Beat command.PersistentFlags().AddGoFlag(flag.CommandLine.Lookup("M")) command.TestCmd.Flags().AddGoFlag(flag.CommandLine.Lookup("modules")) command.SetupCmd.Flags().AddGoFlag(flag.CommandLine.Lookup("modules")) + command.Flags().AddGoFlag(flag.CommandLine.Lookup("ignore-journald-version")) command.AddCommand(cmd.GenModulesCmd(Name, "", buildModulesManager)) command.AddCommand(genGenerateCmd()) return command diff --git a/filebeat/input/journald/input.go b/filebeat/input/journald/input.go index 736569c00bf..c633154a976 100644 --- a/filebeat/input/journald/input.go +++ b/filebeat/input/journald/input.go @@ -21,6 +21,7 @@ package journald import ( "errors" + "flag" "fmt" "strconv" "strings" @@ -42,6 +43,15 @@ import ( "github.com/elastic/elastic-agent-libs/logp" ) +var noVersionCheck bool + +func init() { + flag.BoolVar(&noVersionCheck, + "ignore-journald-version", + false, + "Does not check Journald version when starting the Journald input. This might cause Filebeat to crash!") +} + type journald struct { Backoff time.Duration MaxBackoff time.Duration @@ -92,6 +102,11 @@ func Plugin(log *logp.Logger, store cursor.StateStore) input.Plugin { Manager: m, } + if noVersionCheck { + log.Warn("Journald version check has been DISABLED! Filebeat might crash if Journald version is < 255.") + return p + } + version, err := systemdVersion() if err != nil { configErr := fmt.Errorf("%w: %s", ErrCannotGetSystemdVersion, err) @@ -358,9 +373,14 @@ func parseSystemdVersion(output string) (int, error) { return version, err } -// getSystemdVersionViaDBus foo +// getSystemdVersionViaDBus gets the Systemd version from D-Bus +// +// We get the version by reading the property +// `org.freedesktop.systemd1.Manager.Version`. Even though this property is +// is documented as not being part of the official API and having an unstable +// scheme, on our tests it proved to be stable enough. // -// Version string should not be parsed: +// The Systemd D-Bus documentation states: // // Version encodes the version string of the running systemd // instance. Note that the version string is purely informational, diff --git a/filebeat/input/journald/input_test.go b/filebeat/input/journald/input_test.go index 5c81c52998c..fc4c0796a82 100644 --- a/filebeat/input/journald/input_test.go +++ b/filebeat/input/journald/input_test.go @@ -21,13 +21,21 @@ package journald import ( "context" + "flag" "fmt" + "os" "path" "testing" "github.com/elastic/elastic-agent-libs/mapstr" ) +func TestMain(m *testing.M) { + flag.Parse() + noVersionCheck = true + os.Exit(m.Run()) +} + func TestInputFieldsTranslation(t *testing.T) { // A few random keys to verify keysToCheck := map[string]string{ diff --git a/filebeat/magefile.go b/filebeat/magefile.go index 771741f02a1..f25e021024f 100644 --- a/filebeat/magefile.go +++ b/filebeat/magefile.go @@ -28,6 +28,7 @@ import ( devtools "github.com/elastic/beats/v7/dev-tools/mage" "github.com/elastic/beats/v7/dev-tools/mage/target/build" + "github.com/elastic/beats/v7/dev-tools/mage/target/unittest" filebeat "github.com/elastic/beats/v7/filebeat/scripts/mage" //mage:import @@ -45,6 +46,7 @@ import ( func init() { common.RegisterCheckDeps(Update) test.RegisterDeps(IntegTest) + unittest.RegisterGoTestDeps(TestJournaldInput) devtools.BeatDescription = "Filebeat sends log files to Logstash or directly to Elasticsearch." } @@ -215,15 +217,20 @@ func PythonIntegTest(ctx context.Context) error { return devtools.PythonIntegTestFromHost(devtools.DefaultPythonTestIntegrationFromHostArgs()) } -// TestJournald executes the Journald input tests +// TestJournaldInput executes the Journald input unit tests. +// +// It requires Systemd >= 255 and D-Bus to be installed +// on the host. +// // Use TEST_COVERAGE=true to enable code coverage profiling. // Use RACE_DETECTOR=true to enable the race detector. -func TestJournald(ctx context.Context) error { - utArgs := devtools.DefaultGoTestUnitArgs() - utArgs.Packages = []string{"./input/journald"} +func TestJournaldInput(ctx context.Context) error { if devtools.Platform.GOOS == "linux" { - utArgs.ExtraFlags = append(utArgs.ExtraFlags, "-tags=withjournald") + testArgs := devtools.DefaultGoTestUnitArgs() + testArgs.Packages = []string{"./input/journald"} + testArgs.ExtraFlags = append(testArgs.ExtraFlags, "-tags=withjournald") + return devtools.GoTest(ctx, testArgs) } - return devtools.GoTest(ctx, utArgs) + return nil } diff --git a/libbeat/docs/command-reference.asciidoc b/libbeat/docs/command-reference.asciidoc index 91daaf097be..3574a7144ac 100644 --- a/libbeat/docs/command-reference.asciidoc +++ b/libbeat/docs/command-reference.asciidoc @@ -744,12 +744,19 @@ the end of the file is reached. By default harvesters are closed after The `--once` option is not currently supported with the {filebeat-ref}/filebeat-input-filestream.html[`filestream`] input type. +*`--ignore-journald-version`*:: +When the `--ignore-journald-version` is used, the Journald input +**will not** validate the minimum Systemd version during the input +initialisation. Running the Journald input with an unsupported version +of Systemd might cause {beatname_uc} to crash. endif::[] +ifeval::["{beatname_lc}"=="metricbeat"] *`--system.hostfs MOUNT_POINT`*:: Specifies the mount point of the host's filesystem for use in monitoring a host. This flag is depricated, and an alternate hostfs should be specified via the `hostfs` module config value. +endif::[] ifeval::["{beatname_lc}"=="packetbeat"] From ace2e177e53bfca7680ba78eefd648aa48909ceb Mon Sep 17 00:00:00 2001 From: Tiago Queiroz Date: Thu, 23 May 2024 16:22:02 -0400 Subject: [PATCH 11/14] Refactoring and small code improvements --- filebeat/cmd/root.go | 9 +++++- filebeat/include/list.go | 1 + filebeat/input/journald/input.go | 43 ++++++++++++++++----------- filebeat/input/journald/input_test.go | 18 +++++++++-- x-pack/filebeat/magefile.go | 6 ++-- 5 files changed, 54 insertions(+), 23 deletions(-) diff --git a/filebeat/cmd/root.go b/filebeat/cmd/root.go index 81d87807fcf..98c19c1d6c4 100644 --- a/filebeat/cmd/root.go +++ b/filebeat/cmd/root.go @@ -63,7 +63,14 @@ func Filebeat(inputs beater.PluginFactory, settings instance.Settings) *cmd.Beat command.PersistentFlags().AddGoFlag(flag.CommandLine.Lookup("M")) command.TestCmd.Flags().AddGoFlag(flag.CommandLine.Lookup("modules")) command.SetupCmd.Flags().AddGoFlag(flag.CommandLine.Lookup("modules")) - command.Flags().AddGoFlag(flag.CommandLine.Lookup("ignore-journald-version")) + + // main_test.go calls this function before the Journald input is initialised + // to avoid panics, we check whether the flag is defined before calling + // AddGoFlag + if ignoreSystemdFlag := flag.CommandLine.Lookup("ignore-journald-version"); ignoreSystemdFlag != nil { + command.Flags().AddGoFlag(ignoreSystemdFlag) + } + command.AddCommand(cmd.GenModulesCmd(Name, "", buildModulesManager)) command.AddCommand(genGenerateCmd()) return command diff --git a/filebeat/include/list.go b/filebeat/include/list.go index d0c0ea511c4..6e74633e1ee 100644 --- a/filebeat/include/list.go +++ b/filebeat/include/list.go @@ -28,6 +28,7 @@ import ( // Import packages that perform 'func init()'. _ "github.com/elastic/beats/v7/filebeat/input" _ "github.com/elastic/beats/v7/filebeat/input/container" + _ "github.com/elastic/beats/v7/filebeat/input/journald" _ "github.com/elastic/beats/v7/filebeat/input/log" _ "github.com/elastic/beats/v7/filebeat/input/mqtt" _ "github.com/elastic/beats/v7/filebeat/input/redis" diff --git a/filebeat/input/journald/input.go b/filebeat/input/journald/input.go index c633154a976..161133a85e2 100644 --- a/filebeat/input/journald/input.go +++ b/filebeat/input/journald/input.go @@ -73,16 +73,17 @@ type checkpoint struct { MonotonicTimestamp uint64 } -// errCannotConnectToDBus is returned when the connection to D-Bus -// cannot be established. -var errCannotConnectToDBus = errors.New("cannot connect to D-Bus") - // LocalSystemJournalID is the ID of the local system journal. const localSystemJournalID = "LOCAL_SYSTEM_JOURNAL" const pluginName = "journald" +// ErrSystemdVersionNotSupported is returned by the plugin manager when the +// Systemd version is not supported. var ErrSystemdVersionNotSupported = errors.New("systemd version must be >= 255") + +// ErrCannotGetSystemdVersion is returned by the plugin manager when it is +// not possible to get the Systemd version via D-Bus. var ErrCannotGetSystemdVersion = errors.New("cannot get systemd version") // Plugin creates a new journald input plugin for creating a stateful input. @@ -359,26 +360,34 @@ func (r *readerAdapter) Next() (reader.Message, error) { // - 252.16-1.amzn2023.0.2 // // The function will parse and return the integer before the full stop. -func parseSystemdVersion(output string) (int, error) { - parts := strings.Split(output, ".") - if len(parts) < 2 { - return 0, errors.New("unexpected format for version.") - } - - version, err := strconv.Atoi(parts[0]) - if err != nil { - return 0, fmt.Errorf("cannot parse Systemd version: %s", err) +func parseSystemdVersion(ver string) (int, error) { + // First try, it's just the version number + version, err := strconv.Atoi(ver) + if err == nil { + return version, nil + } + + separators := []string{" ", "."} + // Second try, it's separated by '.' like: 255.6-1-arch + for _, sep := range separators { + parts := strings.Split(ver, sep) + if len(parts) >= 2 { + version, err := strconv.Atoi(parts[0]) + if err == nil { + return version, nil + } + } } - return version, err + return 0, fmt.Errorf("unknown format for Systemd version: '%s'", ver) } // getSystemdVersionViaDBus gets the Systemd version from D-Bus // // We get the version by reading the property // `org.freedesktop.systemd1.Manager.Version`. Even though this property is -// is documented as not being part of the official API and having an unstable -// scheme, on our tests it proved to be stable enough. +// documented as not being part of the official API and having an unstable +// scheme, on our tests it proved to be stable enough for this use. // // The Systemd D-Bus documentation states: // @@ -391,7 +400,7 @@ func parseSystemdVersion(output string) (int, error) { func getSystemdVersionViaDBus() (string, error) { conn, err := dbus.ConnectSessionBus() if err != nil { - return "", fmt.Errorf("%w: %w", errCannotConnectToDBus, err) + return "", fmt.Errorf("cannot connect to D-Bus: %w", err) } defer conn.Close() diff --git a/filebeat/input/journald/input_test.go b/filebeat/input/journald/input_test.go index fc4c0796a82..8b4af9dd200 100644 --- a/filebeat/input/journald/input_test.go +++ b/filebeat/input/journald/input_test.go @@ -23,7 +23,6 @@ import ( "context" "flag" "fmt" - "os" "path" "testing" @@ -31,9 +30,14 @@ import ( ) func TestMain(m *testing.M) { + // We use TestMain because all of our current (May 2024) supported Linux + // distributions ship with a version of Systemd that will cause Filebeat + // input to crash when the journal reached the maximum number of files and + // a new rotation happens. To allow the Journald input to be instantiated + // we need to set a CLI flag and that needs to be done before all tests run. flag.Parse() noVersionCheck = true - os.Exit(m.Run()) + m.Run() } func TestInputFieldsTranslation(t *testing.T) { @@ -93,7 +97,7 @@ func TestInputFieldsTranslation(t *testing.T) { } } -func TestParseJournaldVersion(t *testing.T) { +func TestParseSystemdVersion(t *testing.T) { foo := map[string]struct { data string expected int @@ -110,6 +114,14 @@ func TestParseJournaldVersion(t *testing.T) { expected: 249, data: `249.11-0ubuntu3.12`, }, + "Debain 10": { + expected: 241, + data: "241", + }, + "Red Hat Enterprise Linux 8": { + expected: 239, + data: "239 (239-78.el8)", + }, } for name, tc := range foo { diff --git a/x-pack/filebeat/magefile.go b/x-pack/filebeat/magefile.go index e5b809f3abf..93b6ce41a75 100644 --- a/x-pack/filebeat/magefile.go +++ b/x-pack/filebeat/magefile.go @@ -16,6 +16,7 @@ import ( devtools "github.com/elastic/beats/v7/dev-tools/mage" "github.com/elastic/beats/v7/dev-tools/mage/target/build" + "github.com/elastic/beats/v7/dev-tools/mage/target/unittest" filebeat "github.com/elastic/beats/v7/filebeat/scripts/mage" //mage:import @@ -33,6 +34,7 @@ import ( func init() { common.RegisterCheckDeps(Update) test.RegisterDeps(IntegTest) + unittest.RegisterGoTestDeps(TestJournaldInput) devtools.BeatDescription = "Filebeat sends log files to Logstash or directly to Elasticsearch." devtools.BeatLicense = "Elastic License" @@ -191,9 +193,9 @@ func PythonIntegTest(ctx context.Context) error { // TestJournald executes the Journald input tests // Use TEST_COVERAGE=true to enable code coverage profiling. // Use RACE_DETECTOR=true to enable the race detector. -func TestJournald(ctx context.Context) error { +func TestJournaldInput(ctx context.Context) error { utArgs := devtools.DefaultGoTestUnitArgs() - utArgs.Packages = []string{"./input/journald"} + utArgs.Packages = []string{"../../filebeat/input/journald"} if devtools.Platform.GOOS == "linux" { utArgs.ExtraFlags = append(utArgs.ExtraFlags, "-tags=withjournald") } From 2ec481a8c1c4fedb323ef9e4cf42310f4c39f7f0 Mon Sep 17 00:00:00 2001 From: Tiago Queiroz Date: Mon, 3 Jun 2024 16:33:04 -0400 Subject: [PATCH 12/14] use regexp to parse Systemd version --- filebeat/input/journald/input.go | 40 ++++++++++++-------------------- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/filebeat/input/journald/input.go b/filebeat/input/journald/input.go index 161133a85e2..30c78fbad7e 100644 --- a/filebeat/input/journald/input.go +++ b/filebeat/input/journald/input.go @@ -23,8 +23,8 @@ import ( "errors" "flag" "fmt" + "regexp" "strconv" - "strings" "time" "github.com/coreos/go-systemd/v22/sdjournal" @@ -353,33 +353,23 @@ func (r *readerAdapter) Next() (reader.Message, error) { } // parseSystemdVersion parses the string version from Systemd fetched via D-Bus. -// -// The expected format is: -// -// - 255.6-1-arch -// - 252.16-1.amzn2023.0.2 -// -// The function will parse and return the integer before the full stop. +// The function will parse and return the 3 digit major version, minor version +// and patch are ignored. func parseSystemdVersion(ver string) (int, error) { - // First try, it's just the version number - version, err := strconv.Atoi(ver) - if err == nil { - return version, nil - } - - separators := []string{" ", "."} - // Second try, it's separated by '.' like: 255.6-1-arch - for _, sep := range separators { - parts := strings.Split(ver, sep) - if len(parts) >= 2 { - version, err := strconv.Atoi(parts[0]) - if err == nil { - return version, nil - } - } + re := regexp.MustCompile(`(v)?(?P\d\d\d)(\.)?`) + matches := re.FindStringSubmatch(ver) + if len(matches) == 0 { + return 0, fmt.Errorf("unsupported Systemd version format '%s'", ver) } - return 0, fmt.Errorf("unknown format for Systemd version: '%s'", ver) + // This should never fail because the regexp ensures we're getting a 3-digt + // integer, however, better safe than sorry. + version, err := strconv.Atoi(matches[2]) + if err != nil { + return 0, fmt.Errorf("could not convert '%s' to int: %w", matches[2], err) + } + + return version, nil } // getSystemdVersionViaDBus gets the Systemd version from D-Bus From 381012c21e65e7e73b90fe37b92797bb6d139b19 Mon Sep 17 00:00:00 2001 From: Tiago Queiroz Date: Mon, 3 Jun 2024 16:56:58 -0400 Subject: [PATCH 13/14] Fix typos on comment and changelog --- CHANGELOG.next.asciidoc | 3 +-- filebeat/magefile.go | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.next.asciidoc b/CHANGELOG.next.asciidoc index f65202b3128..a632f9a5598 100644 --- a/CHANGELOG.next.asciidoc +++ b/CHANGELOG.next.asciidoc @@ -289,8 +289,7 @@ https://github.com/elastic/beats/compare/v8.8.1\...main[Check the HEAD diff] - Make HTTP Endpoint input GA. {issue}38979[38979] {pull}39410[39410] - Update CEL mito extensions to v1.12.2. {pull}39755[39755] - Add support for base64-encoded HMAC headers to HTTP Endpoint. {pull}39655[39655] -- Journald input validates the minimum compatible version of Systemd and will fail to start if a Journald input is configured in an incompatible host {issue}34077[34077] {pull}39605[39605] -- Journald input validates the minimum compatible version of Systemd and will fail to start if the Sytemd version in the host is incompatible {issue}34077[34077] {pull}39605[39605] +- Journald input validates the minimum compatible version of Systemd and will fail to start if the Systemd version in the host < v255. {issue}34077[34077] {pull}39605[39605] *Auditbeat* diff --git a/filebeat/magefile.go b/filebeat/magefile.go index f25e021024f..e94969ac5fc 100644 --- a/filebeat/magefile.go +++ b/filebeat/magefile.go @@ -219,7 +219,7 @@ func PythonIntegTest(ctx context.Context) error { // TestJournaldInput executes the Journald input unit tests. // -// It requires Systemd >= 255 and D-Bus to be installed +// It requires Systemd and D-Bus to be installed // on the host. // // Use TEST_COVERAGE=true to enable code coverage profiling. From 1b77913db5c86cc8376a5c5e520fb6c40c6a1cb8 Mon Sep 17 00:00:00 2001 From: Tiago Queiroz Date: Tue, 4 Jun 2024 13:52:38 -0400 Subject: [PATCH 14/14] Use the Systemd DBus --- filebeat/input/journald/input.go | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/filebeat/input/journald/input.go b/filebeat/input/journald/input.go index 30c78fbad7e..33a14bb2579 100644 --- a/filebeat/input/journald/input.go +++ b/filebeat/input/journald/input.go @@ -20,6 +20,7 @@ package journald import ( + "context" "errors" "flag" "fmt" @@ -27,8 +28,8 @@ import ( "strconv" "time" + "github.com/coreos/go-systemd/v22/dbus" "github.com/coreos/go-systemd/v22/sdjournal" - "github.com/godbus/dbus/v5" "github.com/urso/sderr" "github.com/elastic/beats/v7/filebeat/input/journald/pkg/journalfield" @@ -372,12 +373,7 @@ func parseSystemdVersion(ver string) (int, error) { return version, nil } -// getSystemdVersionViaDBus gets the Systemd version from D-Bus -// -// We get the version by reading the property -// `org.freedesktop.systemd1.Manager.Version`. Even though this property is -// documented as not being part of the official API and having an unstable -// scheme, on our tests it proved to be stable enough for this use. +// getSystemdVersionViaDBus gets the Systemd version from sd-bus // // The Systemd D-Bus documentation states: // @@ -388,21 +384,17 @@ func parseSystemdVersion(ver string) (int, error) { // the versioning scheme at any time and it is not part of the API. // Source: https://www.freedesktop.org/wiki/Software/systemd/dbus/ func getSystemdVersionViaDBus() (string, error) { - conn, err := dbus.ConnectSessionBus() + // Get a context with timeout just to be on the safe side + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + conn, err := dbus.NewSystemConnectionContext(ctx) if err != nil { - return "", fmt.Errorf("cannot connect to D-Bus: %w", err) + return "", fmt.Errorf("cannot connect to sd-bus: %w", err) } - defer conn.Close() - obj := conn.Object("org.freedesktop.systemd1", "/org/freedesktop/systemd1") - resp, err := obj.GetProperty("org.freedesktop.systemd1.Manager.Version") + version, err := conn.GetManagerProperty("Version") if err != nil { - return "", fmt.Errorf("cannot get version property from D-Bus %w", err) - } - - version := "" - if err := resp.Store(&version); err != nil { - return "", fmt.Errorf("cannot store Systemd version into Go string: %s", err) + return "", fmt.Errorf("cannot get version property: %w", err) } return version, nil