Adding upstream version 1.34.4.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
e393c3af3f
commit
4978089aab
4963 changed files with 677545 additions and 0 deletions
75
plugins/inputs/tacacs/README.md
Normal file
75
plugins/inputs/tacacs/README.md
Normal file
|
@ -0,0 +1,75 @@
|
|||
# Tacacs Input Plugin
|
||||
|
||||
The Tacacs plugin collects successful tacacs authentication response times
|
||||
from tacacs servers such as Aruba ClearPass, FreeRADIUS or tac_plus (TACACS+).
|
||||
It is primarily meant to monitor how long it takes for the server to fully
|
||||
handle an auth request, including all potential dependent calls (for example
|
||||
to AD servers, or other sources of truth for auth the tacacs server uses).
|
||||
|
||||
## 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
|
||||
# Tacacs plugin collects successful tacacs authentication response times.
|
||||
[[inputs.tacacs]]
|
||||
## An array of Server IPs (or hostnames) and ports to gather from. If none specified, defaults to localhost.
|
||||
# servers = ["127.0.0.1:49"]
|
||||
|
||||
## Request source server IP, normally the server running telegraf.
|
||||
# request_ip = "127.0.0.1"
|
||||
|
||||
## Credentials for tacacs authentication.
|
||||
username = "myuser"
|
||||
password = "mypassword"
|
||||
secret = "mysecret"
|
||||
|
||||
## Maximum time to receive response.
|
||||
# response_timeout = "5s"
|
||||
```
|
||||
|
||||
## Metrics
|
||||
|
||||
- tacacs
|
||||
- tags:
|
||||
- source
|
||||
- fields:
|
||||
- response_status (string, [see below](#field-response_status)))
|
||||
- responsetime_ms (int64 [see below](#field-responsetime_ms)))
|
||||
|
||||
### field `response_status`
|
||||
|
||||
The field "response_status" is either a translated raw code returned
|
||||
by the tacacs server, or filled by telegraf in case of a timeout.
|
||||
|
||||
| Field Value | Raw Code | From | responsetime_ms
|
||||
| -------------------- | ------------ | ------------- | ---------------
|
||||
| AuthenStatusPass | 1 (0x1) | tacacs server | real value
|
||||
| AuthenStatusFail | 2 (0x2) | tacacs server | real value
|
||||
| AuthenStatusGetData | 3 (0x3) | tacacs server | real value
|
||||
| AuthenStatusGetUser | 4 (0x4) | tacacs server | real value
|
||||
| AuthenStatusGetPass | 5 (0x5) | tacacs server | real value
|
||||
| AuthenStatusRestart | 6 (0x6) | tacacs server | real value
|
||||
| AuthenStatusError | 7 (0x7) | tacacs server | real value
|
||||
| AuthenStatusFollow | 33 (0x21) | tacacs server | real value
|
||||
| Timeout | Timeout | telegraf | eq. to response_timeout
|
||||
|
||||
### field `responsetime_ms`
|
||||
|
||||
The field responsetime_ms is response time of the tacacs server
|
||||
in milliseconds of the furthest achieved stage of auth.
|
||||
In case of timeout, its filled by telegraf to be the value of
|
||||
the configured response_timeout.
|
||||
|
||||
## Example Output
|
||||
|
||||
```text
|
||||
tacacs,source=127.0.0.1:49 responsetime_ms=311i,response_status="AuthenStatusPass" 1677526200000000000
|
||||
```
|
15
plugins/inputs/tacacs/sample.conf
Normal file
15
plugins/inputs/tacacs/sample.conf
Normal file
|
@ -0,0 +1,15 @@
|
|||
# Tacacs plugin collects successful tacacs authentication response times.
|
||||
[[inputs.tacacs]]
|
||||
## An array of Server IPs (or hostnames) and ports to gather from. If none specified, defaults to localhost.
|
||||
# servers = ["127.0.0.1:49"]
|
||||
|
||||
## Request source server IP, normally the server running telegraf.
|
||||
# request_ip = "127.0.0.1"
|
||||
|
||||
## Credentials for tacacs authentication.
|
||||
username = "myuser"
|
||||
password = "mypassword"
|
||||
secret = "mysecret"
|
||||
|
||||
## Maximum time to receive response.
|
||||
# response_timeout = "5s"
|
209
plugins/inputs/tacacs/tacacs.go
Normal file
209
plugins/inputs/tacacs/tacacs.go
Normal file
|
@ -0,0 +1,209 @@
|
|||
//go:generate ../../../tools/readme_config_includer/generator
|
||||
package tacacs
|
||||
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/nwaples/tacplus"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/config"
|
||||
"github.com/influxdata/telegraf/plugins/inputs"
|
||||
)
|
||||
|
||||
//go:embed sample.conf
|
||||
var sampleConfig string
|
||||
|
||||
type Tacacs struct {
|
||||
Servers []string `toml:"servers"`
|
||||
Username config.Secret `toml:"username"`
|
||||
Password config.Secret `toml:"password"`
|
||||
Secret config.Secret `toml:"secret"`
|
||||
RequestAddr string `toml:"request_ip"`
|
||||
ResponseTimeout config.Duration `toml:"response_timeout"`
|
||||
Log telegraf.Logger `toml:"-"`
|
||||
clients []tacplus.Client
|
||||
authStart tacplus.AuthenStart
|
||||
}
|
||||
|
||||
func (*Tacacs) SampleConfig() string {
|
||||
return sampleConfig
|
||||
}
|
||||
|
||||
func (t *Tacacs) Init() error {
|
||||
if len(t.Servers) == 0 {
|
||||
t.Servers = []string{"127.0.0.1:49"}
|
||||
}
|
||||
|
||||
if t.Username.Empty() || t.Password.Empty() || t.Secret.Empty() {
|
||||
return errors.New("empty credentials were provided (username, password or secret)")
|
||||
}
|
||||
|
||||
if t.RequestAddr == "" {
|
||||
t.RequestAddr = "127.0.0.1"
|
||||
}
|
||||
if net.ParseIP(t.RequestAddr) == nil {
|
||||
return fmt.Errorf("invalid ip address provided for request_ip: %s", t.RequestAddr)
|
||||
}
|
||||
|
||||
t.clients = make([]tacplus.Client, 0, len(t.Servers))
|
||||
for _, server := range t.Servers {
|
||||
t.clients = append(t.clients, tacplus.Client{
|
||||
Addr: server,
|
||||
ConnConfig: tacplus.ConnConfig{},
|
||||
})
|
||||
}
|
||||
|
||||
t.authStart = tacplus.AuthenStart{
|
||||
Action: tacplus.AuthenActionLogin,
|
||||
AuthenType: tacplus.AuthenTypeASCII,
|
||||
AuthenService: tacplus.AuthenServiceLogin,
|
||||
PrivLvl: 1,
|
||||
Port: "heartbeat",
|
||||
RemAddr: t.RequestAddr,
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Tacacs) Gather(acc telegraf.Accumulator) error {
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for idx := range t.clients {
|
||||
wg.Add(1)
|
||||
go func(client *tacplus.Client) {
|
||||
defer wg.Done()
|
||||
acc.AddError(t.pollServer(acc, client))
|
||||
}(&t.clients[idx])
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
return nil
|
||||
}
|
||||
|
||||
func authenReplyToString(code uint8) string {
|
||||
switch code {
|
||||
case tacplus.AuthenStatusPass:
|
||||
return `AuthenStatusPass`
|
||||
case tacplus.AuthenStatusFail:
|
||||
return `AuthenStatusFail`
|
||||
case tacplus.AuthenStatusGetData:
|
||||
return `AuthenStatusGetData`
|
||||
case tacplus.AuthenStatusGetUser:
|
||||
return `AuthenStatusGetUser`
|
||||
case tacplus.AuthenStatusGetPass:
|
||||
return `AuthenStatusGetPass`
|
||||
case tacplus.AuthenStatusRestart:
|
||||
return `AuthenStatusRestart`
|
||||
case tacplus.AuthenStatusError:
|
||||
return `AuthenStatusError`
|
||||
case tacplus.AuthenStatusFollow:
|
||||
return `AuthenStatusFollow`
|
||||
}
|
||||
return "AuthenStatusUnknown(" + strconv.FormatUint(uint64(code), 10) + ")"
|
||||
}
|
||||
|
||||
func (t *Tacacs) pollServer(acc telegraf.Accumulator, client *tacplus.Client) error {
|
||||
// Create the fields for this metric
|
||||
tags := map[string]string{"source": client.Addr}
|
||||
fields := make(map[string]interface{})
|
||||
|
||||
secret, err := t.Secret.Get()
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting secret failed: %w", err)
|
||||
}
|
||||
defer secret.Destroy()
|
||||
|
||||
client.ConnConfig.Secret = secret.Bytes()
|
||||
|
||||
username, err := t.Username.Get()
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting username failed: %w", err)
|
||||
}
|
||||
defer username.Destroy()
|
||||
|
||||
password, err := t.Password.Get()
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting password failed: %w", err)
|
||||
}
|
||||
defer password.Destroy()
|
||||
|
||||
ctx := context.Background()
|
||||
if t.ResponseTimeout > 0 {
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithTimeout(ctx, time.Duration(t.ResponseTimeout))
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
startTime := time.Now()
|
||||
reply, session, err := client.SendAuthenStart(ctx, &t.authStart)
|
||||
if err != nil {
|
||||
if !errors.Is(err, context.DeadlineExceeded) && !errors.Is(err, os.ErrDeadlineExceeded) {
|
||||
return fmt.Errorf("error on new tacacs authentication start request to %s : %w", client.Addr, err)
|
||||
}
|
||||
fields["responsetime_ms"] = time.Since(startTime).Milliseconds()
|
||||
fields["response_status"] = "Timeout"
|
||||
acc.AddFields("tacacs", fields, tags)
|
||||
return nil
|
||||
}
|
||||
defer session.Close()
|
||||
if reply.Status != tacplus.AuthenStatusGetUser {
|
||||
fields["responsetime_ms"] = time.Since(startTime).Milliseconds()
|
||||
fields["response_status"] = authenReplyToString(reply.Status)
|
||||
acc.AddFields("tacacs", fields, tags)
|
||||
return nil
|
||||
}
|
||||
|
||||
reply, err = session.Continue(ctx, username.String())
|
||||
if err != nil {
|
||||
if !errors.Is(err, context.DeadlineExceeded) && !errors.Is(err, os.ErrDeadlineExceeded) {
|
||||
return fmt.Errorf("error on tacacs authentication continue username request to %s : %w", client.Addr, err)
|
||||
}
|
||||
fields["responsetime_ms"] = time.Since(startTime).Milliseconds()
|
||||
fields["response_status"] = "Timeout"
|
||||
acc.AddFields("tacacs", fields, tags)
|
||||
return nil
|
||||
}
|
||||
if reply.Status != tacplus.AuthenStatusGetPass {
|
||||
fields["responsetime_ms"] = time.Since(startTime).Milliseconds()
|
||||
fields["response_status"] = authenReplyToString(reply.Status)
|
||||
acc.AddFields("tacacs", fields, tags)
|
||||
return nil
|
||||
}
|
||||
|
||||
reply, err = session.Continue(ctx, password.String())
|
||||
if err != nil {
|
||||
if !errors.Is(err, context.DeadlineExceeded) && !errors.Is(err, os.ErrDeadlineExceeded) {
|
||||
return fmt.Errorf("error on tacacs authentication continue password request to %s : %w", client.Addr, err)
|
||||
}
|
||||
fields["responsetime_ms"] = time.Since(startTime).Milliseconds()
|
||||
fields["response_status"] = "Timeout"
|
||||
acc.AddFields("tacacs", fields, tags)
|
||||
return nil
|
||||
}
|
||||
if reply.Status != tacplus.AuthenStatusPass {
|
||||
fields["responsetime_ms"] = time.Since(startTime).Milliseconds()
|
||||
fields["response_status"] = authenReplyToString(reply.Status)
|
||||
acc.AddFields("tacacs", fields, tags)
|
||||
return nil
|
||||
}
|
||||
|
||||
fields["responsetime_ms"] = time.Since(startTime).Milliseconds()
|
||||
fields["response_status"] = authenReplyToString(reply.Status)
|
||||
acc.AddFields("tacacs", fields, tags)
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
inputs.Add("tacacs", func() telegraf.Input {
|
||||
return &Tacacs{ResponseTimeout: config.Duration(time.Second * 5)}
|
||||
})
|
||||
}
|
389
plugins/inputs/tacacs/tacacs_test.go
Normal file
389
plugins/inputs/tacacs/tacacs_test.go
Normal file
|
@ -0,0 +1,389 @@
|
|||
package tacacs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/nwaples/tacplus"
|
||||
"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"
|
||||
"github.com/influxdata/telegraf/testutil"
|
||||
)
|
||||
|
||||
type testRequestHandler map[string]struct {
|
||||
password string
|
||||
args []string
|
||||
}
|
||||
|
||||
func (t testRequestHandler) HandleAuthenStart(_ context.Context, a *tacplus.AuthenStart, s *tacplus.ServerSession) *tacplus.AuthenReply {
|
||||
user := a.User
|
||||
for user == "" {
|
||||
c, err := s.GetUser(context.Background(), "Username:")
|
||||
if err != nil || c.Abort {
|
||||
return nil
|
||||
}
|
||||
user = c.Message
|
||||
}
|
||||
pass := ""
|
||||
for pass == "" {
|
||||
c, err := s.GetPass(context.Background(), "Password:")
|
||||
if err != nil || c.Abort {
|
||||
return nil
|
||||
}
|
||||
pass = c.Message
|
||||
}
|
||||
if u, ok := t[user]; ok && u.password == pass {
|
||||
return &tacplus.AuthenReply{Status: tacplus.AuthenStatusPass}
|
||||
}
|
||||
return &tacplus.AuthenReply{Status: tacplus.AuthenStatusFail}
|
||||
}
|
||||
|
||||
func (t testRequestHandler) HandleAuthorRequest(_ context.Context, a *tacplus.AuthorRequest, _ *tacplus.ServerSession) *tacplus.AuthorResponse {
|
||||
if u, ok := t[a.User]; ok {
|
||||
return &tacplus.AuthorResponse{Status: tacplus.AuthorStatusPassAdd, Arg: u.args}
|
||||
}
|
||||
return &tacplus.AuthorResponse{Status: tacplus.AuthorStatusFail}
|
||||
}
|
||||
|
||||
func (testRequestHandler) HandleAcctRequest(context.Context, *tacplus.AcctRequest, *tacplus.ServerSession) *tacplus.AcctReply {
|
||||
return &tacplus.AcctReply{Status: tacplus.AcctStatusSuccess}
|
||||
}
|
||||
|
||||
func TestTacacsInit(t *testing.T) {
|
||||
var testset = []struct {
|
||||
name string
|
||||
testingTimeout config.Duration
|
||||
serversToTest []string
|
||||
usedUsername config.Secret
|
||||
usedPassword config.Secret
|
||||
usedSecret config.Secret
|
||||
requestAddr string
|
||||
errContains string
|
||||
}{
|
||||
{
|
||||
name: "empty_creds",
|
||||
testingTimeout: config.Duration(time.Second * 5),
|
||||
serversToTest: []string{"foo.bar:80"},
|
||||
usedUsername: config.NewSecret([]byte(``)),
|
||||
usedPassword: config.NewSecret([]byte(`testpassword`)),
|
||||
usedSecret: config.NewSecret([]byte(`testsecret`)),
|
||||
errContains: "empty credentials were provided (username, password or secret)",
|
||||
},
|
||||
{
|
||||
name: "wrong_reqaddress",
|
||||
testingTimeout: config.Duration(time.Second * 5),
|
||||
serversToTest: []string{"foo.bar:80"},
|
||||
usedUsername: config.NewSecret([]byte(`testusername`)),
|
||||
usedPassword: config.NewSecret([]byte(`testpassword`)),
|
||||
usedSecret: config.NewSecret([]byte(`testsecret`)),
|
||||
requestAddr: "257.257.257.257",
|
||||
errContains: "invalid ip address provided for request_ip",
|
||||
},
|
||||
{
|
||||
name: "no_reqaddress_no_servers",
|
||||
testingTimeout: config.Duration(time.Second * 5),
|
||||
usedUsername: config.NewSecret([]byte(`testusername`)),
|
||||
usedPassword: config.NewSecret([]byte(`testpassword`)),
|
||||
usedSecret: config.NewSecret([]byte(`testsecret`)),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range testset {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
plugin := &Tacacs{
|
||||
ResponseTimeout: tt.testingTimeout,
|
||||
Servers: tt.serversToTest,
|
||||
Username: tt.usedUsername,
|
||||
Password: tt.usedPassword,
|
||||
Secret: tt.usedSecret,
|
||||
RequestAddr: tt.requestAddr,
|
||||
Log: testutil.Logger{},
|
||||
}
|
||||
|
||||
err := plugin.Init()
|
||||
if tt.errContains == "" {
|
||||
require.NoError(t, err)
|
||||
if tt.requestAddr == "" {
|
||||
require.Equal(t, "127.0.0.1", plugin.RequestAddr)
|
||||
}
|
||||
if len(tt.serversToTest) == 0 {
|
||||
require.Equal(t, []string{"127.0.0.1:49"}, plugin.Servers)
|
||||
}
|
||||
} else {
|
||||
require.ErrorContains(t, err, tt.errContains)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTacacsLocal(t *testing.T) {
|
||||
testHandler := tacplus.ServerConnHandler{
|
||||
Handler: &testRequestHandler{
|
||||
"testusername": {
|
||||
password: "testpassword",
|
||||
},
|
||||
},
|
||||
ConnConfig: tacplus.ConnConfig{
|
||||
Secret: []byte(`testsecret`),
|
||||
Mux: true,
|
||||
},
|
||||
}
|
||||
l, err := net.Listen("tcp", "localhost:0")
|
||||
require.NoError(t, err, "local net listen failed to start listening")
|
||||
|
||||
srvLocal := l.Addr().String()
|
||||
|
||||
srv := &tacplus.Server{
|
||||
ServeConn: func(nc net.Conn) {
|
||||
testHandler.Serve(nc)
|
||||
},
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := srv.Serve(l); err != nil {
|
||||
t.Logf("local srv.Serve failed to start serving on %s", srvLocal)
|
||||
}
|
||||
}()
|
||||
|
||||
var testset = []struct {
|
||||
name string
|
||||
testingTimeout config.Duration
|
||||
serverToTest []string
|
||||
usedUsername config.Secret
|
||||
usedPassword config.Secret
|
||||
usedSecret config.Secret
|
||||
requestAddr string
|
||||
errContains string
|
||||
reqRespStatus string
|
||||
}{
|
||||
{
|
||||
name: "success_timeout_0s",
|
||||
testingTimeout: config.Duration(0),
|
||||
serverToTest: []string{srvLocal},
|
||||
usedUsername: config.NewSecret([]byte(`testusername`)),
|
||||
usedPassword: config.NewSecret([]byte(`testpassword`)),
|
||||
usedSecret: config.NewSecret([]byte(`testsecret`)),
|
||||
requestAddr: "127.0.0.1",
|
||||
reqRespStatus: "AuthenStatusPass",
|
||||
},
|
||||
{
|
||||
name: "wrongpw",
|
||||
testingTimeout: config.Duration(time.Second * 5),
|
||||
serverToTest: []string{srvLocal},
|
||||
usedUsername: config.NewSecret([]byte(`testusername`)),
|
||||
usedPassword: config.NewSecret([]byte(`WRONGPASSWORD`)),
|
||||
usedSecret: config.NewSecret([]byte(`testsecret`)),
|
||||
requestAddr: "127.0.0.1",
|
||||
reqRespStatus: "AuthenStatusFail",
|
||||
},
|
||||
{
|
||||
name: "wrongsecret",
|
||||
testingTimeout: config.Duration(time.Second * 5),
|
||||
serverToTest: []string{srvLocal},
|
||||
usedUsername: config.NewSecret([]byte(`testusername`)),
|
||||
usedPassword: config.NewSecret([]byte(`testpassword`)),
|
||||
usedSecret: config.NewSecret([]byte(`WRONGSECRET`)),
|
||||
requestAddr: "127.0.0.1",
|
||||
errContains: "error on new tacacs authentication start request to " + srvLocal + " : bad secret or packet",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range testset {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
plugin := &Tacacs{
|
||||
ResponseTimeout: tt.testingTimeout,
|
||||
Servers: tt.serverToTest,
|
||||
Username: tt.usedUsername,
|
||||
Password: tt.usedPassword,
|
||||
Secret: tt.usedSecret,
|
||||
RequestAddr: tt.requestAddr,
|
||||
Log: testutil.Logger{},
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
|
||||
require.NoError(t, plugin.Init())
|
||||
require.NoError(t, plugin.Gather(&acc))
|
||||
|
||||
if tt.errContains == "" {
|
||||
require.Empty(t, acc.Errors)
|
||||
expected := []telegraf.Metric{
|
||||
metric.New(
|
||||
"tacacs",
|
||||
map[string]string{"source": srvLocal},
|
||||
map[string]interface{}{
|
||||
"responsetime_ms": int64(0),
|
||||
"response_status": tt.reqRespStatus,
|
||||
},
|
||||
time.Unix(0, 0),
|
||||
),
|
||||
}
|
||||
options := []cmp.Option{
|
||||
testutil.IgnoreTime(),
|
||||
testutil.IgnoreFields("responsetime_ms"),
|
||||
}
|
||||
testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics(), options...)
|
||||
} else {
|
||||
require.Len(t, acc.Errors, 1)
|
||||
require.ErrorContains(t, acc.FirstError(), tt.errContains)
|
||||
require.Empty(t, acc.GetTelegrafMetrics())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTacacsLocalTimeout(t *testing.T) {
|
||||
testHandler := tacplus.ServerConnHandler{
|
||||
Handler: &testRequestHandler{
|
||||
"testusername": {
|
||||
password: "testpassword",
|
||||
},
|
||||
},
|
||||
ConnConfig: tacplus.ConnConfig{
|
||||
Secret: []byte(`testsecret`),
|
||||
Mux: true,
|
||||
},
|
||||
}
|
||||
l, err := net.Listen("tcp", "localhost:0")
|
||||
require.NoError(t, err, "local net listen failed to start listening")
|
||||
|
||||
srvLocal := l.Addr().String()
|
||||
|
||||
srv := &tacplus.Server{
|
||||
ServeConn: func(nc net.Conn) {
|
||||
testHandler.Serve(nc)
|
||||
},
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := srv.Serve(l); err != nil {
|
||||
t.Logf("local srv.Serve failed to start serving on %s", srvLocal)
|
||||
}
|
||||
}()
|
||||
|
||||
// Initialize the plugin
|
||||
plugin := &Tacacs{
|
||||
ResponseTimeout: config.Duration(time.Microsecond),
|
||||
Servers: []string{"unreachable.test:49"},
|
||||
Username: config.NewSecret([]byte(`testusername`)),
|
||||
Password: config.NewSecret([]byte(`testpassword`)),
|
||||
Secret: config.NewSecret([]byte(`testsecret`)),
|
||||
RequestAddr: "127.0.0.1",
|
||||
Log: &testutil.Logger{},
|
||||
}
|
||||
require.NoError(t, plugin.Init())
|
||||
|
||||
// Try to connect, this will return a metric with the timeout...
|
||||
var acc testutil.Accumulator
|
||||
require.NoError(t, plugin.Gather(&acc))
|
||||
|
||||
expected := []telegraf.Metric{
|
||||
metric.New(
|
||||
"tacacs",
|
||||
map[string]string{"source": "unreachable.test:49"},
|
||||
map[string]interface{}{
|
||||
"response_status": string("Timeout"),
|
||||
"responsetime_ms": int64(0),
|
||||
},
|
||||
time.Unix(0, 0),
|
||||
),
|
||||
}
|
||||
|
||||
options := []cmp.Option{
|
||||
testutil.IgnoreTime(),
|
||||
testutil.IgnoreFields("responsetime_ms"),
|
||||
}
|
||||
|
||||
require.Empty(t, acc.Errors)
|
||||
testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics(), options...)
|
||||
}
|
||||
|
||||
func TestTacacsIntegration(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
container := testutil.Container{
|
||||
Image: "dchidell/docker-tacacs",
|
||||
ExposedPorts: []string{"49/tcp"},
|
||||
WaitingFor: wait.ForAll(
|
||||
wait.ForLog("Starting server..."),
|
||||
),
|
||||
}
|
||||
require.NoError(t, container.Start(), "failed to start container")
|
||||
defer container.Terminate()
|
||||
|
||||
port := container.Ports["49"]
|
||||
|
||||
// Define the testset
|
||||
var testset = []struct {
|
||||
name string
|
||||
testingTimeout config.Duration
|
||||
usedPassword string
|
||||
reqRespStatus string
|
||||
}{
|
||||
{
|
||||
name: "timeout_3s",
|
||||
testingTimeout: config.Duration(time.Second * 3),
|
||||
usedPassword: "cisco",
|
||||
reqRespStatus: "AuthenStatusPass",
|
||||
},
|
||||
{
|
||||
name: "timeout_0s",
|
||||
testingTimeout: config.Duration(0),
|
||||
usedPassword: "cisco",
|
||||
reqRespStatus: "AuthenStatusPass",
|
||||
},
|
||||
{
|
||||
name: "wrong_pw",
|
||||
testingTimeout: config.Duration(time.Second * 5),
|
||||
usedPassword: "wrongpass",
|
||||
reqRespStatus: "AuthenStatusFail",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range testset {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Setup the plugin-under-test
|
||||
plugin := &Tacacs{
|
||||
ResponseTimeout: tt.testingTimeout,
|
||||
Servers: []string{container.Address + ":" + port},
|
||||
Username: config.NewSecret([]byte(`iosadmin`)),
|
||||
Password: config.NewSecret([]byte(tt.usedPassword)),
|
||||
Secret: config.NewSecret([]byte(`ciscotacacskey`)),
|
||||
RequestAddr: "127.0.0.1",
|
||||
Log: testutil.Logger{},
|
||||
}
|
||||
var acc testutil.Accumulator
|
||||
|
||||
// Startup the plugin & Gather
|
||||
require.NoError(t, plugin.Init())
|
||||
require.NoError(t, plugin.Gather(&acc))
|
||||
|
||||
require.NoError(t, acc.FirstError())
|
||||
expected := []telegraf.Metric{
|
||||
metric.New(
|
||||
"tacacs",
|
||||
map[string]string{"source": container.Address + ":" + port},
|
||||
map[string]interface{}{
|
||||
"responsetime_ms": int64(0),
|
||||
"response_status": tt.reqRespStatus,
|
||||
},
|
||||
time.Unix(0, 0),
|
||||
),
|
||||
}
|
||||
options := []cmp.Option{
|
||||
testutil.IgnoreTime(),
|
||||
testutil.IgnoreFields("responsetime_ms"),
|
||||
}
|
||||
testutil.RequireMetricsStructureEqual(t, expected, acc.GetTelegrafMetrics(), options...)
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue