From 2d642d508c52e172dad2e8eaaa326f7f38ff6815 Mon Sep 17 00:00:00 2001 From: Adriel Perkins Date: Fri, 24 Mar 2023 16:48:25 -0400 Subject: [PATCH] feat: add ability to run ldap search --- config.go | 25 ++++++++++++++++++++----- factory.go | 12 +++++++----- go.mod | 1 + go.sum | 2 ++ scraper.go | 44 ++++++++++++++++++++++++++++++++++++-------- scraper_test.go | 1 + testdata/config.yaml | 7 +++++++ 7 files changed, 74 insertions(+), 18 deletions(-) create mode 100644 scraper_test.go diff --git a/config.go b/config.go index 26edb004..737c573a 100644 --- a/config.go +++ b/config.go @@ -7,12 +7,15 @@ import ( // Configuration that is exposed to this ldapreceiver through the OTEL config.yaml type Config struct { - Interval string `mapstructure:"interval"` + Interval string `mapstructure:"interval"` + SearchFilter string `mapstructure:"search_filter"` + Endpoint string `mapstructure:"endpoint"` + BaseDN string `mapstructure:"base_dn"` + InsecureSkipVerify bool `mapstructure:"ignore_tls"` - // TODO: either implement ASM authenticator, or leverage the basic auth extension (preferrable) - //ASMEnabled bool `mapstructure:"asm_enabled"` - //UserSecretName string `mapstructure:"user_secret_name"` - //PassSecretName string `mapstructure:"pass_secret_name"` + // TODO: replace with basic auth through OTel configuration + User string `mapstructure:"user"` + Pw string `mapstructure:"pw"` } // Validate the configuration passed through the OTEL config.yaml @@ -21,5 +24,17 @@ func (cfg *Config) Validate() error { if interval.Seconds() < 10 { return fmt.Errorf("interval must be at least 10 seconds") } + + if cfg.SearchFilter == "" { + return fmt.Errorf("search_filter must be set") + } + + if cfg.BaseDN == "" { + return fmt.Errorf("base_dn must be set") + } + + if cfg.Endpoint == "" { + return fmt.Errorf("endpoint must be set") + } return nil } diff --git a/factory.go b/factory.go index c6331d99..7876fe59 100644 --- a/factory.go +++ b/factory.go @@ -12,10 +12,11 @@ import ( ) const ( - typeStr = "ldap" - defaultInterval = 10 * time.Second - defaultTimeout = 10 * time.Second - stability = component.StabilityLevelAlpha + typeStr = "ldap" + defaultInterval = 10 * time.Second + defaultIgnoreTLS = false + defaultTimeout = 10 * time.Second + stability = component.StabilityLevelAlpha ) var ( @@ -25,7 +26,8 @@ var ( // Create ethe default config based on the const(s) defined above. func createDefaultConfig() component.Config { return &Config{ - Interval: fmt.Sprint(defaultInterval), + Interval: fmt.Sprint(defaultInterval), + InsecureSkipVerify: defaultIgnoreTLS, } } diff --git a/go.mod b/go.mod index 2c281e12..65956fe3 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.20 require ( github.com/go-ldap/ldap/v3 v3.4.4 + github.com/go-playground/assert/v2 v2.2.0 go.opentelemetry.io/collector/component v0.73.0 go.opentelemetry.io/collector/consumer v0.73.0 go.opentelemetry.io/collector/receiver v0.73.0 diff --git a/go.sum b/go.sum index 97d360e2..9fe54dff 100644 --- a/go.sum +++ b/go.sum @@ -61,6 +61,8 @@ github.com/go-ldap/ldap/v3 v3.4.4/go.mod h1:fe1MsuN5eJJ1FeLT/LEBVdWfNWKh459R7aXg github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= diff --git a/scraper.go b/scraper.go index d57a7e17..c27ef093 100644 --- a/scraper.go +++ b/scraper.go @@ -3,6 +3,8 @@ package ldapreceiver import ( "context" "crypto/tls" + "fmt" + "log" "time" "github.com/go-ldap/ldap/v3" @@ -21,16 +23,13 @@ type ldapReceiver struct { // Insantiate the client connection to LDAP func ldapClient(ldapRcvr *ldapReceiver) *ldap.Conn { - //user := strings.Join([]string{"LUV", creds.Username}, "\\") - //pw := creds.Password - // TODO: replace with what is above coming from OTEL config - user := "cn=admin,dc=example,dc=org" - pw := "admin" + // TODO: replace with basic auth through OTel configuration + user := ldapRcvr.config.User + pw := ldapRcvr.config.Pw //#nosec G402 (CWE-295) ignore InsecureSkipVerify TLS setting due to self-signed certificates and network isolation - // TODO: LDAP config should come from OTEL config.yaml - //connection, err := ldap.DialURL("ldaps://:636", ldap.DialWithTLSConfig(&tls.Config{InsecureSkipVerify: SkipTlsVerification})) - connection, err := ldap.DialURL("ldaps://localhost:636", ldap.DialWithTLSConfig(&tls.Config{InsecureSkipVerify: true})) + endpoint := fmt.Sprint("ldaps://", ldapRcvr.config.Endpoint, ":636") + connection, err := ldap.DialURL(endpoint, ldap.DialWithTLSConfig(&tls.Config{InsecureSkipVerify: ldapRcvr.config.InsecureSkipVerify})) if err != nil { ldapRcvr.logger.Sugar().Fatalf("Error dialing ldap server %v", err) } @@ -45,6 +44,34 @@ func ldapClient(ldapRcvr *ldapReceiver) *ldap.Conn { return connection } +// Get the results from an ldapsearch by making a connection to LDAP and returning the search +func getResults(conn *ldap.Conn, ldapRcvr *ldapReceiver) (search *ldap.SearchResult) { + search = performSearch(conn, fmt.Sprint(ldapRcvr.config.SearchFilter), ldapRcvr) + + ldapRcvr.logger.Sugar().Debugf("Number of returned Entries: %d", len(search.Entries)) + + return +} + +// Perform a search query in LDAP and return the result +func performSearch(conn *ldap.Conn, query string, ldapRcvr *ldapReceiver) (result *ldap.SearchResult) { + sr := ldap.NewSearchRequest( + ldapRcvr.config.BaseDN, + ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, + false, + query, + []string{"member"}, + nil, + ) + + result, err := conn.Search(sr) + if err != nil { + log.Fatalf("error with searching request: %v", err) + } + + return result +} + func (ldapRcvr *ldapReceiver) Start(ctx context.Context, host component.Host) error { ldapRcvr.host = host ctx = context.Background() @@ -62,6 +89,7 @@ func (ldapRcvr *ldapReceiver) Start(ctx context.Context, host component.Host) er select { case <-ticker.C: ldapRcvr.logger.Info("Processing metrics..") + getResults(ldapConn, ldapRcvr) case <-ctx.Done(): return } diff --git a/scraper_test.go b/scraper_test.go new file mode 100644 index 00000000..bc9e99fa --- /dev/null +++ b/scraper_test.go @@ -0,0 +1 @@ +package ldapreceiver diff --git a/testdata/config.yaml b/testdata/config.yaml index 751cd10e..48237360 100644 --- a/testdata/config.yaml +++ b/testdata/config.yaml @@ -4,6 +4,13 @@ receivers: grpc: ldap: interval: 10s + search_filter: "(&(objectClass=group)(|MyGroup*))" + endpoint: "localhost" + # TODO: use basic auth + user: "cn=admin,dc=example,dc=org" + pw: "admin" + base_dn: "DC=LUV,DC=AD,DC=SWACORP,DC=com" + #ldap: # interval: "10" # #ldapreceiver: