feat(inputs.radius): Provide setting to set request IP address (#14981)
This commit is contained in:
parent
9d7de5e2b1
commit
fde77790f4
|
|
@ -23,6 +23,10 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
|
||||||
password = "mypassword"
|
password = "mypassword"
|
||||||
secret = "mysecret"
|
secret = "mysecret"
|
||||||
|
|
||||||
|
## Request source server IP, normally the server running telegraf.
|
||||||
|
## This corresponds to Radius' NAS-IP-Address.
|
||||||
|
# request_ip = "127.0.0.1"
|
||||||
|
|
||||||
## Maximum time to receive response.
|
## Maximum time to receive response.
|
||||||
# response_timeout = "5s"
|
# response_timeout = "5s"
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ type Radius struct {
|
||||||
Password config.Secret `toml:"password"`
|
Password config.Secret `toml:"password"`
|
||||||
Secret config.Secret `toml:"secret"`
|
Secret config.Secret `toml:"secret"`
|
||||||
ResponseTimeout config.Duration `toml:"response_timeout"`
|
ResponseTimeout config.Duration `toml:"response_timeout"`
|
||||||
|
RequestIP string `toml:"request_ip"`
|
||||||
Log telegraf.Logger `toml:"-"`
|
Log telegraf.Logger `toml:"-"`
|
||||||
client radius.Client
|
client radius.Client
|
||||||
}
|
}
|
||||||
|
|
@ -44,6 +45,13 @@ func (r *Radius) Init() error {
|
||||||
Retry: 0,
|
Retry: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if r.RequestIP == "" {
|
||||||
|
r.RequestIP = "127.0.0.1"
|
||||||
|
}
|
||||||
|
if net.ParseIP(r.RequestIP) == nil {
|
||||||
|
return fmt.Errorf("invalid ip address provided for request_ip: %s", r.RequestIP)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -107,6 +115,12 @@ func (r *Radius) pollServer(acc telegraf.Accumulator, server string) error {
|
||||||
return fmt.Errorf("setting password for radius auth failed: %w", err)
|
return fmt.Errorf("setting password for radius auth failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if r.RequestIP != "" {
|
||||||
|
if err := rfc2865.NASIPAddress_Set(packet, net.ParseIP(r.RequestIP)); err != nil {
|
||||||
|
return fmt.Errorf("setting NAS IP address for radius auth failed: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Do the radius request
|
// Do the radius request
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
if r.ResponseTimeout > 0 {
|
if r.ResponseTimeout > 0 {
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,87 @@ func TestRadiusLocal(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRadiusNASIP(t *testing.T) {
|
||||||
|
handler := func(w radius.ResponseWriter, r *radius.Request) {
|
||||||
|
username := rfc2865.UserName_GetString(r.Packet)
|
||||||
|
password := rfc2865.UserPassword_GetString(r.Packet)
|
||||||
|
ip := rfc2865.NASIPAddress_Get(r.Packet)
|
||||||
|
|
||||||
|
var code radius.Code
|
||||||
|
if username == "testusername" && password == "testpassword" &&
|
||||||
|
ip.Equal(net.ParseIP("127.0.0.1")) {
|
||||||
|
code = radius.CodeAccessAccept
|
||||||
|
} else {
|
||||||
|
code = radius.CodeAccessReject
|
||||||
|
}
|
||||||
|
if err := w.Write(r.Response(code)); err != nil {
|
||||||
|
require.NoError(t, err, "failed writing radius server response")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup a connection to be able to get a random port
|
||||||
|
conn, err := net.ListenPacket("udp4", "127.0.0.1:0")
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer conn.Close()
|
||||||
|
addr := conn.LocalAddr().String()
|
||||||
|
host, port, err := net.SplitHostPort(addr)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
server := radius.PacketServer{
|
||||||
|
Handler: radius.HandlerFunc(handler),
|
||||||
|
SecretSource: radius.StaticSecretSource([]byte(`testsecret`)),
|
||||||
|
Addr: addr,
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
if err := server.Serve(conn); err != nil {
|
||||||
|
if !errors.Is(err, radius.ErrServerShutdown) {
|
||||||
|
require.NoError(t, err, "local radius server failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
plugin := &Radius{
|
||||||
|
Servers: []string{addr},
|
||||||
|
Username: config.NewSecret([]byte(`testusername`)),
|
||||||
|
Password: config.NewSecret([]byte(`testpassword`)),
|
||||||
|
Secret: config.NewSecret([]byte(`testsecret`)),
|
||||||
|
Log: testutil.Logger{},
|
||||||
|
RequestIP: "127.0.0.1",
|
||||||
|
}
|
||||||
|
require.NoError(t, plugin.Init())
|
||||||
|
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
require.NoError(t, acc.GatherError(plugin.Gather))
|
||||||
|
|
||||||
|
if !acc.HasMeasurement("radius") {
|
||||||
|
t.Errorf("acc.HasMeasurement: expected radius")
|
||||||
|
}
|
||||||
|
require.True(t, acc.HasTag("radius", "source"))
|
||||||
|
require.True(t, acc.HasTag("radius", "source_port"))
|
||||||
|
require.True(t, acc.HasTag("radius", "response_code"))
|
||||||
|
require.Equal(t, host, acc.TagValue("radius", "source"))
|
||||||
|
require.Equal(t, port, acc.TagValue("radius", "source_port"))
|
||||||
|
require.Equal(t, radius.CodeAccessAccept.String(), acc.TagValue("radius", "response_code"))
|
||||||
|
require.True(t, acc.HasInt64Field("radius", "responsetime_ms"))
|
||||||
|
|
||||||
|
if err := server.Shutdown(context.Background()); err != nil {
|
||||||
|
require.NoError(t, err, "failed to properly shutdown local radius server")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvalidRequestIP(t *testing.T) {
|
||||||
|
plugin := &Radius{
|
||||||
|
Servers: []string{"127.0.0.1"},
|
||||||
|
Username: config.NewSecret([]byte(`testusername`)),
|
||||||
|
Password: config.NewSecret([]byte(`testpassword`)),
|
||||||
|
Secret: config.NewSecret([]byte(`testsecret`)),
|
||||||
|
Log: testutil.Logger{},
|
||||||
|
RequestIP: "foobar",
|
||||||
|
}
|
||||||
|
require.Error(t, plugin.Init())
|
||||||
|
}
|
||||||
|
|
||||||
func TestRadiusIntegration(t *testing.T) {
|
func TestRadiusIntegration(t *testing.T) {
|
||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
t.Skip("Skipping integration test in short mode")
|
t.Skip("Skipping integration test in short mode")
|
||||||
|
|
@ -202,3 +283,62 @@ func TestRadiusIntegration(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRadiusIntegrationInvalidSourceIP(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("Skipping integration test in short mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
clients, err := filepath.Abs("testdata/invalidSourceIP/clients.conf")
|
||||||
|
require.NoError(t, err, "determining absolute path of test-data clients.conf failed")
|
||||||
|
authorize, err := filepath.Abs("testdata/invalidSourceIP/mods-config/files/authorize")
|
||||||
|
require.NoError(t, err, "determining absolute path of test-data authorize failed")
|
||||||
|
radiusd, err := filepath.Abs("testdata/invalidSourceIP/radiusd.conf")
|
||||||
|
require.NoError(t, err, "determining absolute path of test-data radiusd.conf failed")
|
||||||
|
|
||||||
|
container := testutil.Container{
|
||||||
|
Image: "freeradius/freeradius-server",
|
||||||
|
ExposedPorts: []string{"1812/udp"},
|
||||||
|
Files: map[string]string{
|
||||||
|
"/etc/raddb/clients.conf": clients,
|
||||||
|
"/etc/raddb/mods-config/files/authorize": authorize,
|
||||||
|
"/etc/raddb/radiusd.conf": radiusd,
|
||||||
|
},
|
||||||
|
WaitingFor: wait.ForAll(
|
||||||
|
wait.ForLog("Ready to process requests"),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
err = container.Start()
|
||||||
|
require.NoError(t, err, "failed to start container")
|
||||||
|
defer container.Terminate()
|
||||||
|
|
||||||
|
port := container.Ports["1812"]
|
||||||
|
plugin := &Radius{
|
||||||
|
ResponseTimeout: config.Duration(time.Second * 1),
|
||||||
|
Servers: []string{container.Address + ":" + port},
|
||||||
|
Username: config.NewSecret([]byte(`testusername`)),
|
||||||
|
Password: config.NewSecret([]byte(`testpassword`)),
|
||||||
|
Secret: config.NewSecret([]byte(`testsecret`)),
|
||||||
|
Log: testutil.Logger{},
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := testutil.MustMetric(
|
||||||
|
"radius",
|
||||||
|
map[string]string{
|
||||||
|
"source": container.Address,
|
||||||
|
"source_port": port,
|
||||||
|
"response_code": "timeout",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"responsetime_ms": 1000,
|
||||||
|
},
|
||||||
|
time.Time{},
|
||||||
|
)
|
||||||
|
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
require.NoError(t, plugin.Init())
|
||||||
|
require.NoError(t, plugin.Gather(&acc))
|
||||||
|
metrics := acc.GetTelegrafMetrics()
|
||||||
|
require.Len(t, metrics, 1)
|
||||||
|
testutil.RequireMetricEqual(t, expected, metrics[0], testutil.IgnoreTime())
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,5 +7,9 @@
|
||||||
password = "mypassword"
|
password = "mypassword"
|
||||||
secret = "mysecret"
|
secret = "mysecret"
|
||||||
|
|
||||||
|
## Request source server IP, normally the server running telegraf.
|
||||||
|
## This corresponds to Radius' NAS-IP-Address.
|
||||||
|
# request_ip = "127.0.0.1"
|
||||||
|
|
||||||
## Maximum time to receive response.
|
## Maximum time to receive response.
|
||||||
# response_timeout = "5s"
|
# response_timeout = "5s"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
client localtest {
|
||||||
|
ipaddr = 10.123.123.0/24
|
||||||
|
secret = testsecret
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
testusername Cleartext-Password := "testpassword"
|
||||||
|
|
@ -0,0 +1,118 @@
|
||||||
|
prefix = /usr
|
||||||
|
exec_prefix = /usr
|
||||||
|
sysconfdir = /etc
|
||||||
|
localstatedir = /var
|
||||||
|
sbindir = ${exec_prefix}/sbin
|
||||||
|
logdir = /var/log/freeradius
|
||||||
|
raddbdir = /etc/freeradius
|
||||||
|
radacctdir = ${logdir}/radacct
|
||||||
|
|
||||||
|
name = freeradius
|
||||||
|
|
||||||
|
confdir = ${raddbdir}
|
||||||
|
modconfdir = ${confdir}/mods-config
|
||||||
|
certdir = ${confdir}/certs
|
||||||
|
cadir = ${confdir}/certs
|
||||||
|
run_dir = ${localstatedir}/run/${name}
|
||||||
|
|
||||||
|
db_dir = ${raddbdir}
|
||||||
|
|
||||||
|
libdir = /usr/lib/freeradius
|
||||||
|
|
||||||
|
pidfile = ${run_dir}/${name}.pid
|
||||||
|
|
||||||
|
|
||||||
|
max_request_time = 30
|
||||||
|
|
||||||
|
cleanup_delay = 5
|
||||||
|
|
||||||
|
max_requests = 16384
|
||||||
|
|
||||||
|
hostname_lookups = no
|
||||||
|
|
||||||
|
|
||||||
|
log {
|
||||||
|
destination = stdout
|
||||||
|
|
||||||
|
colourise = yes
|
||||||
|
|
||||||
|
file = ${logdir}/radius.log
|
||||||
|
|
||||||
|
syslog_facility = daemon
|
||||||
|
|
||||||
|
stripped_names = no
|
||||||
|
|
||||||
|
auth = yes
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
auth_badpass = yes
|
||||||
|
auth_goodpass = yes
|
||||||
|
|
||||||
|
|
||||||
|
msg_denied = "You are already logged in - access denied"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
checkrad = ${sbindir}/checkrad
|
||||||
|
|
||||||
|
ENV {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
security {
|
||||||
|
|
||||||
|
user = freerad
|
||||||
|
group = freerad
|
||||||
|
|
||||||
|
allow_core_dumps = no
|
||||||
|
|
||||||
|
max_attributes = 200
|
||||||
|
|
||||||
|
reject_delay = 1
|
||||||
|
|
||||||
|
status_server = yes
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy_requests = yes
|
||||||
|
$INCLUDE proxy.conf
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$INCLUDE clients.conf
|
||||||
|
|
||||||
|
|
||||||
|
thread pool {
|
||||||
|
start_servers = 5
|
||||||
|
|
||||||
|
max_servers = 32
|
||||||
|
|
||||||
|
min_spare_servers = 3
|
||||||
|
max_spare_servers = 10
|
||||||
|
|
||||||
|
|
||||||
|
max_requests_per_server = 0
|
||||||
|
|
||||||
|
|
||||||
|
auto_limit_acct = no
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
modules {
|
||||||
|
|
||||||
|
|
||||||
|
$INCLUDE mods-enabled/
|
||||||
|
}
|
||||||
|
|
||||||
|
instantiate {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
policy {
|
||||||
|
$INCLUDE policy.d/
|
||||||
|
}
|
||||||
|
|
||||||
|
$INCLUDE sites-enabled/
|
||||||
Loading…
Reference in New Issue