feat(inputs.ldap): Add LDAP input plugin supporting OpenLDAP and 389ds (#13995)
This commit is contained in:
parent
474aff588e
commit
0c1e21398e
|
|
@ -0,0 +1,5 @@
|
||||||
|
//go:build !custom || inputs || inputs.ldap
|
||||||
|
|
||||||
|
package all
|
||||||
|
|
||||||
|
import _ "github.com/influxdata/telegraf/plugins/inputs/ldap" // register plugin
|
||||||
|
|
@ -0,0 +1,114 @@
|
||||||
|
package ldap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-ldap/ldap/v3"
|
||||||
|
"github.com/influxdata/telegraf"
|
||||||
|
"github.com/influxdata/telegraf/metric"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Empty mappings are identity mappings
|
||||||
|
var attrMap389ds = map[string]string{
|
||||||
|
"addentryops": "add_operations",
|
||||||
|
"anonymousbinds": "anonymous_binds",
|
||||||
|
"bindsecurityerrors": "bind_security_errors",
|
||||||
|
"bytesrecv": "bytes_received",
|
||||||
|
"bytessent": "bytes_sent",
|
||||||
|
"cacheentries": "cache_entries",
|
||||||
|
"cachehits": "cache_hits",
|
||||||
|
"chainings": "",
|
||||||
|
"compareops": "compare_operations",
|
||||||
|
"connections": "",
|
||||||
|
"connectionsinmaxthreads": "connections_in_max_threads",
|
||||||
|
"connectionsmaxthreadscount": "connections_max_threads",
|
||||||
|
"copyentries": "copy_entries",
|
||||||
|
"currentconnections": "current_connections",
|
||||||
|
"currentconnectionsatmaxthreads": "current_connections_at_max_threads",
|
||||||
|
"dtablesize": "",
|
||||||
|
"entriesreturned": "entries_returned",
|
||||||
|
"entriessent": "entries_sent",
|
||||||
|
"errors": "",
|
||||||
|
"inops": "in_operations",
|
||||||
|
"listops": "list_operations",
|
||||||
|
"removeentryops": "delete_operations",
|
||||||
|
"masterentries": "master_entries",
|
||||||
|
"maxthreadsperconnhits": "maxthreads_per_conn_hits",
|
||||||
|
"modifyentryops": "modify_operations",
|
||||||
|
"modifyrdnops": "modrdn_operations",
|
||||||
|
"nbackends": "backends",
|
||||||
|
"onelevelsearchops": "onelevel_search_operations",
|
||||||
|
"opscompleted": "operations_completed",
|
||||||
|
"opsinitiated": "operations_initiated",
|
||||||
|
"readops": "read_operations",
|
||||||
|
"readwaiters": "read_waiters",
|
||||||
|
"referrals": "referrals",
|
||||||
|
"referralsreturned": "referrals_returned",
|
||||||
|
"searchops": "search_operations",
|
||||||
|
"securityerrors": "security_errors",
|
||||||
|
"simpleauthbinds": "simpleauth_binds",
|
||||||
|
"slavehits": "slave_hits",
|
||||||
|
"strongauthbinds": "strongauth_binds",
|
||||||
|
"threads": "",
|
||||||
|
"totalconnections": "total_connections",
|
||||||
|
"unauthbinds": "unauth_binds",
|
||||||
|
"wholesubtreesearchops": "wholesubtree_search_operations",
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LDAP) new389dsConfig() []request {
|
||||||
|
attributes := make([]string, 0, len(attrMap389ds))
|
||||||
|
for k := range attrMap389ds {
|
||||||
|
attributes = append(attributes, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
req := ldap.NewSearchRequest(
|
||||||
|
"cn=Monitor",
|
||||||
|
ldap.ScopeWholeSubtree,
|
||||||
|
ldap.NeverDerefAliases,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
"(objectClass=*)",
|
||||||
|
attributes,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
return []request{{req, l.convert389ds}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LDAP) convert389ds(result *ldap.SearchResult, ts time.Time) []telegraf.Metric {
|
||||||
|
tags := map[string]string{
|
||||||
|
"server": l.host,
|
||||||
|
"port": l.port,
|
||||||
|
}
|
||||||
|
fields := make(map[string]interface{})
|
||||||
|
for _, entry := range result.Entries {
|
||||||
|
for _, attr := range entry.Attributes {
|
||||||
|
if len(attr.Values[0]) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Map the attribute-name to the field-name
|
||||||
|
name := attrMap389ds[attr.Name]
|
||||||
|
if name == "" {
|
||||||
|
name = attr.Name
|
||||||
|
}
|
||||||
|
// Reverse the name if requested
|
||||||
|
if l.ReverseFieldNames {
|
||||||
|
parts := strings.Split(name, "_")
|
||||||
|
for i, j := 0, len(parts)-1; i < j; i, j = i+1, j-1 {
|
||||||
|
parts[i], parts[j] = parts[j], parts[i]
|
||||||
|
}
|
||||||
|
name = strings.Join(parts, "_")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the number
|
||||||
|
if v, err := strconv.ParseInt(attr.Values[0], 10, 64); err == nil {
|
||||||
|
fields[name] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m := metric.New("389ds", tags, fields, ts)
|
||||||
|
return []telegraf.Metric{m}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
# LDAP Input Plugin
|
||||||
|
|
||||||
|
This plugin gathers metrics from LDAP servers' monitoring (`cn=Monitor`)
|
||||||
|
backend. Currently this plugin supports [OpenLDAP](https://www.openldap.org/)
|
||||||
|
and [389ds](https://www.port389.org/) servers.
|
||||||
|
|
||||||
|
## 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
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
```toml @sample.conf
|
||||||
|
# LDAP monitoring plugin
|
||||||
|
[[inputs.openldap]]
|
||||||
|
## Server to monitor
|
||||||
|
## The scheme determines the mode to use for connection with
|
||||||
|
## ldap://... -- unencrypted (non-TLS) connection
|
||||||
|
## ldaps://... -- TLS connection
|
||||||
|
## starttls://... -- StartTLS connection
|
||||||
|
## If no port is given, the default ports, 389 for ldap and starttls and
|
||||||
|
## 636 for ldaps, are used.
|
||||||
|
server = "ldap://localhost"
|
||||||
|
|
||||||
|
## Server dialect, can be "openldap" or "389ds"
|
||||||
|
# dialect = "openldap"
|
||||||
|
|
||||||
|
# DN and password to bind with
|
||||||
|
## If bind_dn is empty an anonymous bind is performed.
|
||||||
|
bind_dn = ""
|
||||||
|
bind_password = ""
|
||||||
|
|
||||||
|
## Reverse the field names constructed from the monitoring DN
|
||||||
|
# reverse_field_names = false
|
||||||
|
|
||||||
|
## Optional TLS Config
|
||||||
|
## Trusted root certificates for server
|
||||||
|
# tls_ca = "/path/to/cafile"
|
||||||
|
## Used for TLS client certificate authentication
|
||||||
|
# tls_cert = "/path/to/certfile"
|
||||||
|
## Used for TLS client certificate authentication
|
||||||
|
# tls_key = "/path/to/keyfile"
|
||||||
|
## Send the specified TLS server name via SNI
|
||||||
|
# tls_server_name = "kubernetes.example.com"
|
||||||
|
## Use TLS but skip chain & host verification
|
||||||
|
# insecure_skip_verify = false
|
||||||
|
```
|
||||||
|
|
||||||
|
To use this plugin you must enable the monitoring backend/plugin of your LDAP
|
||||||
|
server. See
|
||||||
|
[OpenLDAP](https://www.openldap.org/devel/admin/monitoringslapd.html) or 389ds
|
||||||
|
documentation for details.
|
||||||
|
|
||||||
|
## Metrics
|
||||||
|
|
||||||
|
Depending on the server dialect, different metrics are produced. The metrics
|
||||||
|
are usually named according to the selected dialect.
|
||||||
|
|
||||||
|
### Tags
|
||||||
|
|
||||||
|
- server -- Server name or IP
|
||||||
|
- port -- Port used for connecting
|
||||||
|
|
||||||
|
## Example Output
|
||||||
|
|
||||||
|
Using the `openldap` dialect
|
||||||
|
|
||||||
|
```text
|
||||||
|
openldap,server=localhost,port=389 operations_bind_initiated=10i,operations_unbind_initiated=6i,operations_modrdn_completed=0i,operations_delete_initiated=0i,operations_add_completed=2i,operations_delete_completed=0i,operations_abandon_completed=0i,statistics_entries=1516i,threads_open=2i,threads_active=1i,waiters_read=1i,operations_modify_completed=0i,operations_extended_initiated=4i,threads_pending=0i,operations_search_initiated=36i,operations_compare_initiated=0i,connections_max_file_descriptors=4096i,operations_modify_initiated=0i,operations_modrdn_initiated=0i,threads_max=16i,time_uptime=6017i,connections_total=1037i,connections_current=1i,operations_add_initiated=2i,statistics_bytes=162071i,operations_unbind_completed=6i,operations_abandon_initiated=0i,statistics_pdu=1566i,threads_max_pending=0i,threads_backload=1i,waiters_write=0i,operations_bind_completed=10i,operations_search_completed=35i,operations_compare_completed=0i,operations_extended_completed=4i,statistics_referrals=0i,threads_starting=0i 1516912070000000000
|
||||||
|
```
|
||||||
|
|
||||||
|
Using the `389ds` dialect
|
||||||
|
|
||||||
|
```text
|
||||||
|
389ds,port=32805,server=localhost add_operations=0i,anonymous_binds=0i,backends=0i,bind_security_errors=0i,bytes_received=0i,bytes_sent=256i,cache_entries=0i,cache_hits=0i,chainings=0i,compare_operations=0i,connections=1i,connections_in_max_threads=0i,connections_max_threads=0i,copy_entries=0i,current_connections=1i,current_connections_at_max_threads=0i,delete_operations=0i,dtablesize=63936i,entries_returned=2i,entries_sent=2i,errors=2i,in_operations=11i,list_operations=0i,maxthreads_per_conn_hits=0i,modify_operations=1i,modrdn_operations=0i,onelevel_search_operations=0i,operations_completed=10i,operations_initiated=11i,read_operations=0i,read_waiters=0i,referrals=0i,referrals_returned=0i,search_operations=3i,security_errors=0i,simpleauth_binds=1i,strongauth_binds=2i,threads=17i,total_connections=4i,unauth_binds=0i,wholesubtree_search_operations=1i 1695637234047087280
|
||||||
|
```
|
||||||
|
|
@ -0,0 +1,178 @@
|
||||||
|
//go:generate ../../../tools/readme_config_includer/generator
|
||||||
|
package ldap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
_ "embed"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-ldap/ldap/v3"
|
||||||
|
|
||||||
|
"github.com/influxdata/telegraf"
|
||||||
|
"github.com/influxdata/telegraf/config"
|
||||||
|
commontls "github.com/influxdata/telegraf/plugins/common/tls"
|
||||||
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed sample.conf
|
||||||
|
var sampleConfig string
|
||||||
|
|
||||||
|
type LDAP struct {
|
||||||
|
Server string `toml:"server"`
|
||||||
|
Dialect string `toml:"dialect"`
|
||||||
|
BindDn string `toml:"bind_dn"`
|
||||||
|
BindPassword config.Secret `toml:"bind_password"`
|
||||||
|
ReverseFieldNames bool `toml:"reverse_field_names"`
|
||||||
|
commontls.ClientConfig
|
||||||
|
|
||||||
|
tlsCfg *tls.Config
|
||||||
|
requests []request
|
||||||
|
mode string
|
||||||
|
host string
|
||||||
|
port string
|
||||||
|
}
|
||||||
|
|
||||||
|
type request struct {
|
||||||
|
query *ldap.SearchRequest
|
||||||
|
convert func(*ldap.SearchResult, time.Time) []telegraf.Metric
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*LDAP) SampleConfig() string {
|
||||||
|
return sampleConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LDAP) Init() error {
|
||||||
|
if l.Server == "" {
|
||||||
|
l.Server = "ldap://localhost:389"
|
||||||
|
}
|
||||||
|
|
||||||
|
u, err := url.Parse(l.Server)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("parsing server failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the server setting and set the defaults
|
||||||
|
var tlsEnable bool
|
||||||
|
switch u.Scheme {
|
||||||
|
case "ldap":
|
||||||
|
if u.Port() == "" {
|
||||||
|
u.Host = u.Host + ":389"
|
||||||
|
}
|
||||||
|
tlsEnable = false
|
||||||
|
case "starttls":
|
||||||
|
if u.Port() == "" {
|
||||||
|
u.Host = u.Host + ":389"
|
||||||
|
}
|
||||||
|
tlsEnable = true
|
||||||
|
case "ldaps":
|
||||||
|
if u.Port() == "" {
|
||||||
|
u.Host = u.Host + ":636"
|
||||||
|
}
|
||||||
|
tlsEnable = true
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("invalid scheme: %q", u.Scheme)
|
||||||
|
}
|
||||||
|
l.mode = u.Scheme
|
||||||
|
l.Server = u.Host
|
||||||
|
l.host, l.port = u.Hostname(), u.Port()
|
||||||
|
|
||||||
|
// Force TLS depending on the selected mode
|
||||||
|
l.ClientConfig.Enable = &tlsEnable
|
||||||
|
|
||||||
|
// Setup TLS configuration
|
||||||
|
tlsCfg, err := l.ClientConfig.TLSConfig()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("creating TLS config failed: %w", err)
|
||||||
|
}
|
||||||
|
l.tlsCfg = tlsCfg
|
||||||
|
|
||||||
|
// Initialize the search request(s)
|
||||||
|
switch l.Dialect {
|
||||||
|
case "", "openldap":
|
||||||
|
l.requests = l.newOpenLDAPConfig()
|
||||||
|
case "389ds":
|
||||||
|
l.requests = l.new389dsConfig()
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("invalid dialect %q", l.Dialect)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LDAP) Gather(acc telegraf.Accumulator) error {
|
||||||
|
// Connect
|
||||||
|
conn, err := l.connect()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("connection failed: %w", err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
// Query the server
|
||||||
|
for _, req := range l.requests {
|
||||||
|
now := time.Now()
|
||||||
|
result, err := conn.Search(req.query)
|
||||||
|
if err != nil {
|
||||||
|
acc.AddError(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect metrics
|
||||||
|
for _, m := range req.convert(result, now) {
|
||||||
|
acc.AddMetric(m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LDAP) connect() (*ldap.Conn, error) {
|
||||||
|
var conn *ldap.Conn
|
||||||
|
switch l.mode {
|
||||||
|
case "ldap":
|
||||||
|
var err error
|
||||||
|
conn, err = ldap.Dial("tcp", l.Server)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
case "ldaps":
|
||||||
|
var err error
|
||||||
|
conn, err = ldap.DialTLS("tcp", l.Server, l.tlsCfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
case "starttls":
|
||||||
|
var err error
|
||||||
|
conn, err = ldap.Dial("tcp", l.Server)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := conn.StartTLS(l.tlsCfg); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("invalid tls_mode: %s", l.mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.BindDn == "" && l.BindPassword.Empty() {
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind username and password
|
||||||
|
passwd, err := l.BindPassword.Get()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("getting password failed: %w", err)
|
||||||
|
}
|
||||||
|
defer passwd.Destroy()
|
||||||
|
|
||||||
|
if err := conn.Bind(l.BindDn, passwd.String()); err != nil {
|
||||||
|
return nil, fmt.Errorf("binding credentials failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
inputs.Add("ldap", func() telegraf.Input { return &LDAP{} })
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,573 @@
|
||||||
|
package ldap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/docker/go-connections/nat"
|
||||||
|
"github.com/go-ldap/ldap/v3"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/testcontainers/testcontainers-go/wait"
|
||||||
|
|
||||||
|
"github.com/influxdata/telegraf"
|
||||||
|
"github.com/influxdata/telegraf/config"
|
||||||
|
"github.com/influxdata/telegraf/metric"
|
||||||
|
commontls "github.com/influxdata/telegraf/plugins/common/tls"
|
||||||
|
"github.com/influxdata/telegraf/testutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
servicePortOpenLDAP = "1389"
|
||||||
|
servicePortOpenLDAPSecure = "1636"
|
||||||
|
|
||||||
|
servicePort389DS = "3389"
|
||||||
|
servicePort389DSSecure = "3636"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMockResult(t *testing.T) {
|
||||||
|
// mock a query result
|
||||||
|
mockSearchResult := &ldap.SearchResult{
|
||||||
|
Entries: []*ldap.Entry{
|
||||||
|
{
|
||||||
|
DN: "cn=Total,cn=Connections,cn=Monitor",
|
||||||
|
Attributes: []*ldap.EntryAttribute{{Name: "monitorCounter", Values: []string{"1"}}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Referrals: []string{},
|
||||||
|
Controls: []ldap.Control{},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup the plugin
|
||||||
|
plugin := &LDAP{}
|
||||||
|
require.NoError(t, plugin.Init())
|
||||||
|
|
||||||
|
// Setup the expectations
|
||||||
|
expected := []telegraf.Metric{
|
||||||
|
metric.New(
|
||||||
|
"openldap",
|
||||||
|
map[string]string{
|
||||||
|
"server": "localhost",
|
||||||
|
"port": "389",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"total_connections": int64(1),
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve the converter
|
||||||
|
requests := plugin.newOpenLDAPConfig()
|
||||||
|
require.Len(t, requests, 1)
|
||||||
|
converter := requests[0].convert
|
||||||
|
require.NotNil(t, converter)
|
||||||
|
|
||||||
|
// Test metric conversion
|
||||||
|
actual := converter(mockSearchResult, time.Unix(0, 0))
|
||||||
|
testutil.RequireMetricsEqual(t, expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvalidTLSMode(t *testing.T) {
|
||||||
|
plugin := &LDAP{
|
||||||
|
Server: "foo://localhost",
|
||||||
|
}
|
||||||
|
require.ErrorContains(t, plugin.Init(), "invalid scheme")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNoConnection(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("Skipping integration test in short mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup the plugin
|
||||||
|
plugin := &LDAP{Server: "ldap://nosuchhost"}
|
||||||
|
require.NoError(t, plugin.Init())
|
||||||
|
|
||||||
|
// Collect the metrics and compare
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
require.ErrorContains(t, plugin.Gather(&acc), "connection failed")
|
||||||
|
require.Empty(t, acc.GetTelegrafMetrics())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOpenLDAPIntegration(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("Skipping integration test in short mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the docker container
|
||||||
|
container := testutil.Container{
|
||||||
|
Image: "bitnami/openldap",
|
||||||
|
ExposedPorts: []string{servicePortOpenLDAP},
|
||||||
|
Env: map[string]string{
|
||||||
|
"LDAP_ADMIN_USERNAME": "manager",
|
||||||
|
"LDAP_ADMIN_PASSWORD": "secret",
|
||||||
|
},
|
||||||
|
WaitingFor: wait.ForAll(
|
||||||
|
wait.ForLog("slapd starting"),
|
||||||
|
wait.ForListeningPort(nat.Port(servicePortOpenLDAP)),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
require.NoError(t, container.Start(), "failed to start container")
|
||||||
|
defer container.Terminate()
|
||||||
|
|
||||||
|
// Setup the plugin
|
||||||
|
port := container.Ports[servicePortOpenLDAP]
|
||||||
|
plugin := &LDAP{
|
||||||
|
Server: "ldap://" + container.Address + ":" + port,
|
||||||
|
BindDn: "CN=manager,DC=example,DC=org",
|
||||||
|
BindPassword: config.NewSecret([]byte("secret")),
|
||||||
|
}
|
||||||
|
require.NoError(t, plugin.Init())
|
||||||
|
|
||||||
|
// Setup the expectations
|
||||||
|
expected := []telegraf.Metric{
|
||||||
|
metric.New(
|
||||||
|
"openldap",
|
||||||
|
map[string]string{
|
||||||
|
"server": container.Address,
|
||||||
|
"port": port,
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"abandon_operations_completed": int64(0),
|
||||||
|
"abandon_operations_initiated": int64(0),
|
||||||
|
"active_threads": int64(0),
|
||||||
|
"add_operations_completed": int64(0),
|
||||||
|
"add_operations_initiated": int64(0),
|
||||||
|
"backload_threads": int64(0),
|
||||||
|
"bind_operations_completed": int64(0),
|
||||||
|
"bind_operations_initiated": int64(0),
|
||||||
|
"bytes_statistics": int64(0),
|
||||||
|
"compare_operations_completed": int64(0),
|
||||||
|
"compare_operations_initiated": int64(0),
|
||||||
|
"current_connections": int64(0),
|
||||||
|
"delete_operations_completed": int64(0),
|
||||||
|
"delete_operations_initiated": int64(0),
|
||||||
|
"entries_statistics": int64(0),
|
||||||
|
"extended_operations_completed": int64(0),
|
||||||
|
"extended_operations_initiated": int64(0),
|
||||||
|
"max_file_descriptors_connections": int64(0),
|
||||||
|
"max_pending_threads": int64(0),
|
||||||
|
"max_threads": int64(0),
|
||||||
|
"modify_operations_completed": int64(0),
|
||||||
|
"modify_operations_initiated": int64(0),
|
||||||
|
"modrdn_operations_completed": int64(0),
|
||||||
|
"modrdn_operations_initiated": int64(0),
|
||||||
|
"open_threads": int64(0),
|
||||||
|
"pdu_statistics": int64(0),
|
||||||
|
"pending_threads": int64(0),
|
||||||
|
"read_waiters": int64(0),
|
||||||
|
"referrals_statistics": int64(0),
|
||||||
|
"search_operations_completed": int64(0),
|
||||||
|
"search_operations_initiated": int64(0),
|
||||||
|
"starting_threads": int64(0),
|
||||||
|
"total_connections": int64(0),
|
||||||
|
"unbind_operations_completed": int64(0),
|
||||||
|
"unbind_operations_initiated": int64(0),
|
||||||
|
"uptime_time": int64(0),
|
||||||
|
"write_waiters": int64(0),
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect the metrics and compare
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
require.NoError(t, plugin.Gather(&acc))
|
||||||
|
|
||||||
|
actual := acc.GetTelegrafMetrics()
|
||||||
|
testutil.RequireMetricsStructureEqual(t, expected, actual, testutil.IgnoreTime())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOpenLDAPReverseDNIntegration(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("Skipping integration test in short mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the docker container
|
||||||
|
container := testutil.Container{
|
||||||
|
Image: "bitnami/openldap",
|
||||||
|
ExposedPorts: []string{servicePortOpenLDAP},
|
||||||
|
Env: map[string]string{
|
||||||
|
"LDAP_ADMIN_USERNAME": "manager",
|
||||||
|
"LDAP_ADMIN_PASSWORD": "secret",
|
||||||
|
},
|
||||||
|
WaitingFor: wait.ForAll(
|
||||||
|
wait.ForLog("slapd starting"),
|
||||||
|
wait.ForListeningPort(nat.Port(servicePortOpenLDAP)),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
require.NoError(t, container.Start(), "failed to start container")
|
||||||
|
defer container.Terminate()
|
||||||
|
|
||||||
|
// Setup the plugin
|
||||||
|
port := container.Ports[servicePortOpenLDAP]
|
||||||
|
plugin := &LDAP{
|
||||||
|
Server: "ldap://" + container.Address + ":" + port,
|
||||||
|
BindDn: "CN=manager,DC=example,DC=org",
|
||||||
|
BindPassword: config.NewSecret([]byte("secret")),
|
||||||
|
ReverseFieldNames: true,
|
||||||
|
}
|
||||||
|
require.NoError(t, plugin.Init())
|
||||||
|
|
||||||
|
// Setup the expectations
|
||||||
|
expected := []telegraf.Metric{
|
||||||
|
metric.New(
|
||||||
|
"openldap",
|
||||||
|
map[string]string{
|
||||||
|
"server": container.Address,
|
||||||
|
"port": port,
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"connections_max_file_descriptors": int64(0),
|
||||||
|
"connections_total": int64(0),
|
||||||
|
"connections_current": int64(0),
|
||||||
|
"operations_bind_initiated": int64(0),
|
||||||
|
"operations_bind_completed": int64(0),
|
||||||
|
"operations_unbind_initiated": int64(0),
|
||||||
|
"operations_unbind_completed": int64(0),
|
||||||
|
"operations_search_initiated": int64(0),
|
||||||
|
"operations_search_completed": int64(0),
|
||||||
|
"operations_compare_initiated": int64(0),
|
||||||
|
"operations_compare_completed": int64(0),
|
||||||
|
"operations_modify_initiated": int64(0),
|
||||||
|
"operations_modify_completed": int64(0),
|
||||||
|
"operations_modrdn_initiated": int64(0),
|
||||||
|
"operations_modrdn_completed": int64(0),
|
||||||
|
"operations_add_initiated": int64(0),
|
||||||
|
"operations_add_completed": int64(0),
|
||||||
|
"operations_delete_initiated": int64(0),
|
||||||
|
"operations_delete_completed": int64(0),
|
||||||
|
"operations_abandon_initiated": int64(0),
|
||||||
|
"operations_abandon_completed": int64(0),
|
||||||
|
"operations_extended_initiated": int64(0),
|
||||||
|
"operations_extended_completed": int64(0),
|
||||||
|
"statistics_bytes": int64(0),
|
||||||
|
"statistics_pdu": int64(0),
|
||||||
|
"statistics_entries": int64(0),
|
||||||
|
"statistics_referrals": int64(0),
|
||||||
|
"threads_max": int64(0),
|
||||||
|
"threads_max_pending": int64(0),
|
||||||
|
"threads_open": int64(0),
|
||||||
|
"threads_starting": int64(0),
|
||||||
|
"threads_active": int64(0),
|
||||||
|
"threads_pending": int64(0),
|
||||||
|
"threads_backload": int64(0),
|
||||||
|
"time_uptime": int64(0),
|
||||||
|
"waiters_read": int64(0),
|
||||||
|
"waiters_write": int64(0),
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect the metrics and compare
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
require.NoError(t, plugin.Gather(&acc))
|
||||||
|
|
||||||
|
actual := acc.GetTelegrafMetrics()
|
||||||
|
testutil.RequireMetricsStructureEqual(t, expected, actual, testutil.IgnoreTime())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOpenLDAPStartTLSIntegration(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("Skipping integration test in short mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup PKI for TLS testing
|
||||||
|
pkiPaths, err := testutil.NewPKI("../../../testutil/pki").AbsolutePaths()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Start the docker container
|
||||||
|
container := testutil.Container{
|
||||||
|
Image: "bitnami/openldap",
|
||||||
|
ExposedPorts: []string{servicePortOpenLDAP},
|
||||||
|
Env: map[string]string{
|
||||||
|
"LDAP_ADMIN_USERNAME": "manager",
|
||||||
|
"LDAP_ADMIN_PASSWORD": "secret",
|
||||||
|
"LDAP_ENABLE_TLS": "yes",
|
||||||
|
"LDAP_TLS_CA_FILE": "server.pem",
|
||||||
|
"LDAP_TLS_CERT_FILE": "server.crt",
|
||||||
|
"LDAP_TLS_KEY_FILE": "server.key",
|
||||||
|
},
|
||||||
|
BindMounts: map[string]string{
|
||||||
|
"/server.pem": pkiPaths.ServerPem,
|
||||||
|
"/server.crt": pkiPaths.ServerCert,
|
||||||
|
"/server.key": pkiPaths.ServerKey,
|
||||||
|
},
|
||||||
|
WaitingFor: wait.ForAll(
|
||||||
|
wait.ForLog("slapd starting"),
|
||||||
|
wait.ForListeningPort(nat.Port(servicePortOpenLDAP)),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
require.NoError(t, container.Start(), "failed to start container")
|
||||||
|
defer container.Terminate()
|
||||||
|
|
||||||
|
// Setup the plugin
|
||||||
|
port := container.Ports[servicePortOpenLDAP]
|
||||||
|
plugin := &LDAP{
|
||||||
|
Server: "starttls://" + container.Address + ":" + port,
|
||||||
|
BindDn: "CN=manager,DC=example,DC=org",
|
||||||
|
BindPassword: config.NewSecret([]byte("secret")),
|
||||||
|
ClientConfig: commontls.ClientConfig{
|
||||||
|
TLSCA: pkiPaths.ClientCert,
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
require.NoError(t, plugin.Init())
|
||||||
|
|
||||||
|
// Setup the expectations
|
||||||
|
expected := []telegraf.Metric{
|
||||||
|
metric.New(
|
||||||
|
"openldap",
|
||||||
|
map[string]string{
|
||||||
|
"server": container.Address,
|
||||||
|
"port": port,
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"abandon_operations_completed": int64(0),
|
||||||
|
"abandon_operations_initiated": int64(0),
|
||||||
|
"active_threads": int64(0),
|
||||||
|
"add_operations_completed": int64(0),
|
||||||
|
"add_operations_initiated": int64(0),
|
||||||
|
"backload_threads": int64(0),
|
||||||
|
"bind_operations_completed": int64(0),
|
||||||
|
"bind_operations_initiated": int64(0),
|
||||||
|
"bytes_statistics": int64(0),
|
||||||
|
"compare_operations_completed": int64(0),
|
||||||
|
"compare_operations_initiated": int64(0),
|
||||||
|
"current_connections": int64(0),
|
||||||
|
"delete_operations_completed": int64(0),
|
||||||
|
"delete_operations_initiated": int64(0),
|
||||||
|
"entries_statistics": int64(0),
|
||||||
|
"extended_operations_completed": int64(0),
|
||||||
|
"extended_operations_initiated": int64(0),
|
||||||
|
"max_file_descriptors_connections": int64(0),
|
||||||
|
"max_pending_threads": int64(0),
|
||||||
|
"max_threads": int64(0),
|
||||||
|
"modify_operations_completed": int64(0),
|
||||||
|
"modify_operations_initiated": int64(0),
|
||||||
|
"modrdn_operations_completed": int64(0),
|
||||||
|
"modrdn_operations_initiated": int64(0),
|
||||||
|
"open_threads": int64(0),
|
||||||
|
"pdu_statistics": int64(0),
|
||||||
|
"pending_threads": int64(0),
|
||||||
|
"read_waiters": int64(0),
|
||||||
|
"referrals_statistics": int64(0),
|
||||||
|
"search_operations_completed": int64(0),
|
||||||
|
"search_operations_initiated": int64(0),
|
||||||
|
"starting_threads": int64(0),
|
||||||
|
"total_connections": int64(0),
|
||||||
|
"unbind_operations_completed": int64(0),
|
||||||
|
"unbind_operations_initiated": int64(0),
|
||||||
|
"uptime_time": int64(0),
|
||||||
|
"write_waiters": int64(0),
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect the metrics and compare
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
require.NoError(t, plugin.Gather(&acc))
|
||||||
|
|
||||||
|
actual := acc.GetTelegrafMetrics()
|
||||||
|
testutil.RequireMetricsStructureEqual(t, expected, actual, testutil.IgnoreTime())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOpenLDAPLDAPSIntegration(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("Skipping integration test in short mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup PKI for TLS testing
|
||||||
|
pkiPaths, err := testutil.NewPKI("../../../testutil/pki").AbsolutePaths()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Start the docker container
|
||||||
|
container := testutil.Container{
|
||||||
|
Image: "bitnami/openldap",
|
||||||
|
ExposedPorts: []string{servicePortOpenLDAPSecure},
|
||||||
|
Env: map[string]string{
|
||||||
|
"LDAP_ADMIN_USERNAME": "manager",
|
||||||
|
"LDAP_ADMIN_PASSWORD": "secret",
|
||||||
|
"LDAP_ENABLE_TLS": "yes",
|
||||||
|
"LDAP_TLS_CA_FILE": "server.pem",
|
||||||
|
"LDAP_TLS_CERT_FILE": "server.crt",
|
||||||
|
"LDAP_TLS_KEY_FILE": "server.key",
|
||||||
|
},
|
||||||
|
BindMounts: map[string]string{
|
||||||
|
"/server.pem": pkiPaths.ServerPem,
|
||||||
|
"/server.crt": pkiPaths.ServerCert,
|
||||||
|
"/server.key": pkiPaths.ServerKey,
|
||||||
|
},
|
||||||
|
WaitingFor: wait.ForAll(
|
||||||
|
wait.ForLog("slapd starting"),
|
||||||
|
wait.ForListeningPort(nat.Port(servicePortOpenLDAPSecure)),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
require.NoError(t, container.Start(), "failed to start container")
|
||||||
|
defer container.Terminate()
|
||||||
|
|
||||||
|
// Setup the plugin
|
||||||
|
port := container.Ports[servicePortOpenLDAPSecure]
|
||||||
|
plugin := &LDAP{
|
||||||
|
Server: "ldaps://" + container.Address + ":" + port,
|
||||||
|
BindDn: "CN=manager,DC=example,DC=org",
|
||||||
|
BindPassword: config.NewSecret([]byte("secret")),
|
||||||
|
ClientConfig: commontls.ClientConfig{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
require.NoError(t, plugin.Init())
|
||||||
|
|
||||||
|
// Setup the expectations
|
||||||
|
expected := []telegraf.Metric{
|
||||||
|
metric.New(
|
||||||
|
"openldap",
|
||||||
|
map[string]string{
|
||||||
|
"server": container.Address,
|
||||||
|
"port": port,
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"abandon_operations_completed": int64(0),
|
||||||
|
"abandon_operations_initiated": int64(0),
|
||||||
|
"active_threads": int64(0),
|
||||||
|
"add_operations_completed": int64(0),
|
||||||
|
"add_operations_initiated": int64(0),
|
||||||
|
"backload_threads": int64(0),
|
||||||
|
"bind_operations_completed": int64(0),
|
||||||
|
"bind_operations_initiated": int64(0),
|
||||||
|
"bytes_statistics": int64(0),
|
||||||
|
"compare_operations_completed": int64(0),
|
||||||
|
"compare_operations_initiated": int64(0),
|
||||||
|
"current_connections": int64(0),
|
||||||
|
"delete_operations_completed": int64(0),
|
||||||
|
"delete_operations_initiated": int64(0),
|
||||||
|
"entries_statistics": int64(0),
|
||||||
|
"extended_operations_completed": int64(0),
|
||||||
|
"extended_operations_initiated": int64(0),
|
||||||
|
"max_file_descriptors_connections": int64(0),
|
||||||
|
"max_pending_threads": int64(0),
|
||||||
|
"max_threads": int64(0),
|
||||||
|
"modify_operations_completed": int64(0),
|
||||||
|
"modify_operations_initiated": int64(0),
|
||||||
|
"modrdn_operations_completed": int64(0),
|
||||||
|
"modrdn_operations_initiated": int64(0),
|
||||||
|
"open_threads": int64(0),
|
||||||
|
"pdu_statistics": int64(0),
|
||||||
|
"pending_threads": int64(0),
|
||||||
|
"read_waiters": int64(0),
|
||||||
|
"referrals_statistics": int64(0),
|
||||||
|
"search_operations_completed": int64(0),
|
||||||
|
"search_operations_initiated": int64(0),
|
||||||
|
"starting_threads": int64(0),
|
||||||
|
"total_connections": int64(0),
|
||||||
|
"unbind_operations_completed": int64(0),
|
||||||
|
"unbind_operations_initiated": int64(0),
|
||||||
|
"uptime_time": int64(0),
|
||||||
|
"write_waiters": int64(0),
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect the metrics and compare
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
require.NoError(t, plugin.Gather(&acc))
|
||||||
|
|
||||||
|
actual := acc.GetTelegrafMetrics()
|
||||||
|
testutil.RequireMetricsStructureEqual(t, expected, actual, testutil.IgnoreTime())
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test389dsIntegration(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("Skipping integration test in short mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the docker container
|
||||||
|
container := testutil.Container{
|
||||||
|
Image: "389ds/dirsrv",
|
||||||
|
ExposedPorts: []string{servicePort389DS},
|
||||||
|
Env: map[string]string{
|
||||||
|
"DS_DM_PASSWORD": "secret",
|
||||||
|
},
|
||||||
|
WaitingFor: wait.ForAll(
|
||||||
|
wait.ForLog("389-ds-container started"),
|
||||||
|
wait.ForListeningPort(nat.Port(servicePort389DS)),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
require.NoError(t, container.Start(), "failed to start container")
|
||||||
|
defer container.Terminate()
|
||||||
|
|
||||||
|
// Setup the plugin
|
||||||
|
port := container.Ports[servicePort389DS]
|
||||||
|
plugin := &LDAP{
|
||||||
|
Server: "ldap://" + container.Address + ":" + port,
|
||||||
|
Dialect: "389ds",
|
||||||
|
BindDn: "cn=Directory Manager",
|
||||||
|
BindPassword: config.NewSecret([]byte("secret")),
|
||||||
|
}
|
||||||
|
require.NoError(t, plugin.Init())
|
||||||
|
|
||||||
|
// Setup the expectations
|
||||||
|
expected := []telegraf.Metric{
|
||||||
|
metric.New(
|
||||||
|
"389ds",
|
||||||
|
map[string]string{
|
||||||
|
"server": container.Address,
|
||||||
|
"port": port,
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"add_operations": int64(0),
|
||||||
|
"anonymous_binds": int64(0),
|
||||||
|
"backends": int64(0),
|
||||||
|
"bind_security_errors": int64(0),
|
||||||
|
"bytes_received": int64(0),
|
||||||
|
"bytes_sent": int64(0),
|
||||||
|
"cache_entries": int64(0),
|
||||||
|
"cache_hits": int64(0),
|
||||||
|
"chainings": int64(0),
|
||||||
|
"compare_operations": int64(0),
|
||||||
|
"connections": int64(0),
|
||||||
|
"connections_in_max_threads": int64(0),
|
||||||
|
"connections_max_threads": int64(0),
|
||||||
|
"copy_entries": int64(0),
|
||||||
|
"current_connections": int64(0),
|
||||||
|
"current_connections_at_max_threads": int64(0),
|
||||||
|
"delete_operations": int64(0),
|
||||||
|
"dtablesize": int64(0),
|
||||||
|
"entries_returned": int64(0),
|
||||||
|
"entries_sent": int64(0),
|
||||||
|
"errors": int64(0),
|
||||||
|
"in_operations": int64(0),
|
||||||
|
"list_operations": int64(0),
|
||||||
|
"maxthreads_per_conn_hits": int64(0),
|
||||||
|
"modify_operations": int64(0),
|
||||||
|
"modrdn_operations": int64(0),
|
||||||
|
"onelevel_search_operations": int64(0),
|
||||||
|
"operations_completed": int64(0),
|
||||||
|
"operations_initiated": int64(0),
|
||||||
|
"read_operations": int64(0),
|
||||||
|
"read_waiters": int64(0),
|
||||||
|
"referrals": int64(0),
|
||||||
|
"referrals_returned": int64(0),
|
||||||
|
"search_operations": int64(0),
|
||||||
|
"security_errors": int64(0),
|
||||||
|
"simpleauth_binds": int64(0),
|
||||||
|
"strongauth_binds": int64(0),
|
||||||
|
"threads": int64(0),
|
||||||
|
"total_connections": int64(0),
|
||||||
|
"unauth_binds": int64(0),
|
||||||
|
"wholesubtree_search_operations": int64(0),
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect the metrics and compare
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
require.NoError(t, plugin.Gather(&acc))
|
||||||
|
|
||||||
|
actual := acc.GetTelegrafMetrics()
|
||||||
|
testutil.RequireMetricsStructureEqual(t, expected, actual, testutil.IgnoreTime())
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
package ldap
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-ldap/ldap/v3"
|
||||||
|
"github.com/influxdata/telegraf"
|
||||||
|
"github.com/influxdata/telegraf/metric"
|
||||||
|
)
|
||||||
|
|
||||||
|
var attrMapOpenLDAP = map[string]string{
|
||||||
|
"monitorCounter": "",
|
||||||
|
"monitoredInfo": "",
|
||||||
|
"monitorOpInitiated": "_initiated",
|
||||||
|
"monitorOpCompleted": "_completed",
|
||||||
|
"olmMDBPagesMax": "_mdb_pages_max",
|
||||||
|
"olmMDBPagesUsed": "_mdb_pages_used",
|
||||||
|
"olmMDBPagesFree": "_mdb_pages_free",
|
||||||
|
"olmMDBReadersMax": "_mdb_readers_max",
|
||||||
|
"olmMDBReadersUsed": "_mdb_readers_used",
|
||||||
|
"olmMDBEntries": "_mdb_entries",
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LDAP) newOpenLDAPConfig() []request {
|
||||||
|
req := ldap.NewSearchRequest(
|
||||||
|
"cn=Monitor",
|
||||||
|
ldap.ScopeWholeSubtree,
|
||||||
|
ldap.NeverDerefAliases,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
"(|(objectClass=monitorCounterObject)(objectClass=monitorOperation)(objectClass=monitoredObject))",
|
||||||
|
[]string{"monitorCounter", "monitorOpInitiated", "monitorOpCompleted", "monitoredInfo"},
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
return []request{{req, l.convertOpenLDAP}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LDAP) convertOpenLDAP(result *ldap.SearchResult, ts time.Time) []telegraf.Metric {
|
||||||
|
tags := map[string]string{
|
||||||
|
"server": l.host,
|
||||||
|
"port": l.port,
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := make(map[string]interface{})
|
||||||
|
for _, entry := range result.Entries {
|
||||||
|
prefix := openLDAPAttrConvertDN(entry.DN, l.ReverseFieldNames)
|
||||||
|
for _, attr := range entry.Attributes {
|
||||||
|
if len(attr.Values[0]) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if v, err := strconv.ParseInt(attr.Values[0], 10, 64); err == nil {
|
||||||
|
fields[prefix+attrMapOpenLDAP[attr.Name]] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m := metric.New("openldap", tags, fields, ts)
|
||||||
|
return []telegraf.Metric{m}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert a DN to a field prefix, eg cn=Read,cn=Waiters,cn=Monitor becomes waiters_read
|
||||||
|
// Assumes the last part of the DN is cn=Monitor and we want to drop it
|
||||||
|
func openLDAPAttrConvertDN(dn string, reverse bool) string {
|
||||||
|
// Normalize DN
|
||||||
|
prefix := strings.TrimSpace(dn)
|
||||||
|
prefix = strings.ToLower(prefix)
|
||||||
|
prefix = strings.ReplaceAll(prefix, " ", "_")
|
||||||
|
prefix = strings.ReplaceAll(prefix, "cn=", "")
|
||||||
|
|
||||||
|
// Filter the base
|
||||||
|
parts := strings.Split(prefix, ",")
|
||||||
|
for i, p := range parts {
|
||||||
|
if p == "monitor" {
|
||||||
|
parts = append(parts[:i], parts[i+1:]...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if reverse {
|
||||||
|
for i, j := 0, len(parts)-1; i < j; i, j = i+1, j-1 {
|
||||||
|
parts[i], parts[j] = parts[j], parts[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strings.Join(parts, "_")
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
# LDAP monitoring plugin
|
||||||
|
[[inputs.openldap]]
|
||||||
|
## Server to monitor
|
||||||
|
## The scheme determines the mode to use for connection with
|
||||||
|
## ldap://... -- unencrypted (non-TLS) connection
|
||||||
|
## ldaps://... -- TLS connection
|
||||||
|
## starttls://... -- StartTLS connection
|
||||||
|
## If no port is given, the default ports, 389 for ldap and starttls and
|
||||||
|
## 636 for ldaps, are used.
|
||||||
|
server = "ldap://localhost"
|
||||||
|
|
||||||
|
## Server dialect, can be "openldap" or "389ds"
|
||||||
|
# dialect = "openldap"
|
||||||
|
|
||||||
|
# DN and password to bind with
|
||||||
|
## If bind_dn is empty an anonymous bind is performed.
|
||||||
|
bind_dn = ""
|
||||||
|
bind_password = ""
|
||||||
|
|
||||||
|
## Reverse the field names constructed from the monitoring DN
|
||||||
|
# reverse_field_names = false
|
||||||
|
|
||||||
|
## Optional TLS Config
|
||||||
|
## Trusted root certificates for server
|
||||||
|
# tls_ca = "/path/to/cafile"
|
||||||
|
## Used for TLS client certificate authentication
|
||||||
|
# tls_cert = "/path/to/certfile"
|
||||||
|
## Used for TLS client certificate authentication
|
||||||
|
# tls_key = "/path/to/keyfile"
|
||||||
|
## Send the specified TLS server name via SNI
|
||||||
|
# tls_server_name = "kubernetes.example.com"
|
||||||
|
## Use TLS but skip chain & host verification
|
||||||
|
# insecure_skip_verify = false
|
||||||
|
|
@ -5,10 +5,18 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf/plugins/common/tls"
|
"github.com/influxdata/telegraf/plugins/common/tls"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type PKIPaths struct {
|
||||||
|
ServerPem string
|
||||||
|
ServerCert string
|
||||||
|
ServerKey string
|
||||||
|
ClientCert string
|
||||||
|
}
|
||||||
|
|
||||||
type pki struct {
|
type pki struct {
|
||||||
keyPath string
|
keyPath string
|
||||||
}
|
}
|
||||||
|
|
@ -112,6 +120,32 @@ func (p *pki) ServerCertAndKeyPath() string {
|
||||||
return path.Join(p.keyPath, "server.pem")
|
return path.Join(p.keyPath, "server.pem")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *pki) AbsolutePaths() (*PKIPaths, error) {
|
||||||
|
tlsPem, err := filepath.Abs(p.ServerCertAndKeyPath())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tlsCert, err := filepath.Abs(p.ServerCertPath())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tlsKey, err := filepath.Abs(p.ServerKeyPath())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cert, err := filepath.Abs(p.ClientCertPath())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &PKIPaths{
|
||||||
|
ServerPem: tlsPem,
|
||||||
|
ServerCert: tlsCert,
|
||||||
|
ServerKey: tlsKey,
|
||||||
|
ClientCert: cert,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func readCertificate(filename string) string {
|
func readCertificate(filename string) string {
|
||||||
file, err := os.Open(filename)
|
file, err := os.Open(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue