diff --git a/.fixtures.yml b/.fixtures.yml index 86570f89..02c2eb77 100644 --- a/.fixtures.yml +++ b/.fixtures.yml @@ -5,3 +5,4 @@ fixtures: logrotate: "puppet/logrotate" rsyslog: "saz/rsyslog" stdlib: "puppetlabs/stdlib" + systemd: "camptocamp/systemd" diff --git a/README.md b/README.md index 79fb6287..073db6d9 100644 --- a/README.md +++ b/README.md @@ -193,6 +193,22 @@ Whether the control socket should be read-only or read-write. Choose from `ro`, } ``` +#### `freeradius::radsniff` + +The `freeradius::radsniff` class configures and runs the [RADSNIFF](https://freeradius.org/radiusd/man/radsniff.html) service. +It requires freeradius-utils to be installed, so will fail if `utils_support` is not enabled on the `freeradius` class. + +Note: This is only supported on RedHat like systems at present. + +##### `options` +Command line options to be passed to radsniff. Quotes are escaped +```puppet + # Enable radsniff, with a filter, sending data to collectd (requires freeradius to be compiled for this) + class { 'freeradius::radsniff': + options => '-m -p1812,1813 -O unix:/var/run/collectd.sock -N freeradius -W 10 -i eth0 -f "src not 192.0.2.1"', + } +``` + ### Resources #### `freeradius::attr` diff --git a/manifests/init.pp b/manifests/init.pp index 3a9f41c9..0fe113da 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -240,7 +240,7 @@ concat { "${freeradius::fr_basepath}/dictionary": owner => 'root', group => $freeradius::fr_group, - mode => '0640', + mode => '0644', require => [Package[$freeradius::fr_package], Group[$freeradius::fr_group]], } concat::fragment { 'dictionary_header': diff --git a/manifests/params.pp b/manifests/params.pp index 46e47b80..b3f6912d 100644 --- a/manifests/params.pp +++ b/manifests/params.pp @@ -172,4 +172,14 @@ } $radacctdir = "\${logdir}/radacct" + + # Default radsniff environment file location + $fr_radsniff_envfile = $::osfamily ? { + 'RedHat' => '/etc/sysconfig/radsniff', + 'Debian' => '/etc/defaults/radsniff', + default => undef, + } + + # Default radsniff pid file location + $fr_radsniff_pidfile = "/var/run/${fr_service}/radsniff.pid" } diff --git a/manifests/radsniff.pp b/manifests/radsniff.pp new file mode 100644 index 00000000..52c1c3b1 --- /dev/null +++ b/manifests/radsniff.pp @@ -0,0 +1,57 @@ +# @summary configure and run radsniff +# +# @param envfile path to the environment file, used by the systemd unit +# @param options commandline options passed to radsniff when it runs +# @param +class freeradius::radsniff ( + Optional[String] $envfile = undef, + String $options = '', + Optional[String] $pidfile = undef, +) inherits freeradius::params { + unless $::freeradius::utils_support { + fail('freeradius::radsniff requires freeradius have utils_support enabled') + } + + # Calculate the envfile to use - specified, then calculated, then error if none + if $envfile { + $final_envfile = $envfile + } else { + if $freeradius::radsniff::fr_radsniff_envfile { + $final_envfile = $freeradius::radsniff::fr_radsniff_envfile + } else { + fail('freeradius::radsniff requires envfile to be explicitly set on this OS') + } + } + + # Calculate the pidfile to use - specified, then calculated, then error if none + if $pidfile { + $final_pidfile = $pidfile + } else { + if $freeradius::radsniff::fr_radsniff_pidfile { + $final_pidfile = $freeradius::radsniff::fr_radsniff_pidfile + } else { + fail('freeradius::radsniff requires pidfile to be explicitly set on this OS') + } + } + + $escaped_cmd = $options.regsubst('"','\\\\"','G') + + file { $final_envfile: + content => @("SYSCONFIG"), + RADSNIFF_OPTIONS="${escaped_cmd}" + | SYSCONFIG + owner => 'root', + group => 'root', + mode => '0644', + require => Package['freeradius-utils'], + } + ~> service { 'radsniff': + ensure => running, + enable => true, + } + + systemd::unit_file {'radsniff.service': + content => template('freeradius/radsniff.service.erb'), + notify => Service['radsniff'], + } +} diff --git a/metadata.json b/metadata.json index 6e39d8e3..fb050441 100644 --- a/metadata.json +++ b/metadata.json @@ -27,6 +27,10 @@ { "name": "puppetlabs/concat", "version_requirement": ">=1.0.0 <7.0.0" + }, + { + "name": "camptocamp/systemd", + "version_requirement": ">=2.0.0 <3.0.0" } ], "operatingsystem_support": [ diff --git a/spec/classes/freeradius_spec.rb b/spec/classes/freeradius_spec.rb index 138bdfb5..e97136e3 100644 --- a/spec/classes/freeradius_spec.rb +++ b/spec/classes/freeradius_spec.rb @@ -151,7 +151,7 @@ is_expected.to contain_concat('/etc/raddb/dictionary') .with( 'group' => 'radiusd', - 'mode' => '0640', + 'mode' => '0644', 'owner' => 'root', ) .that_requires('Package[freeradius]') diff --git a/spec/classes/radsniff_spec.rb b/spec/classes/radsniff_spec.rb new file mode 100644 index 00000000..5687907b --- /dev/null +++ b/spec/classes/radsniff_spec.rb @@ -0,0 +1,123 @@ +require 'spec_helper' + +describe 'freeradius::radsniff' do + on_supported_os.each do |os, os_facts| + include_context 'freeradius_with_utils' + + context "on #{os}" do + let(:facts) { os_facts } + + let(:params) do + { + options: 'radsniff cmd "line" options', + } + end + + let(:pre_condition) do + precondition = case os_facts[:osfamily] + when 'RedHat' + 'class freeradius::params { + $fr_basepath = "/etc/raddb" + $fr_radsniff_pidfile = "/var/run/radiusd/radsniff.pid" + $fr_radsniff_envfile = "/etc/sysconfig/radsniff" + } + include freeradius::params' + when 'Debian' + 'class freeradius::params { + $fr_basepath = "/etc/freeradius" + $fr_radsniff_pidfile = "/var/run/freeradius/radsniff.pid" + $fr_radsniff_envfile = "/etc/defaults/radsniff" + } + include freeradius::params' + else + 'class freeradius::params { + $fr_basepath = "/etc/raddb" + $fr_radsniff_pidfile = "/var/run/radiusd/radsniff.pid" + $fr_radsniff_envfile = undef + } + include freeradius::params' + end + + super().push(precondition) + end + + if os_facts[:osfamily] =~ %r{^RedHat|Debian$} + it do + is_expected.to contain_service('radsniff') + .with_ensure('running') + .with_enable(true) + end + end + + case os_facts[:osfamily] + when 'RedHat' + it do + is_expected.to contain_file('/etc/sysconfig/radsniff') + .with_content(%r{RADSNIFF_OPTIONS="radsniff cmd \\"line\\" options"}) + .that_notifies('Service[radsniff]') + .that_requires('Package[freeradius-utils]') + end + + it do + is_expected.to contain_systemd__unit_file('radsniff.service') + .with_content(%r{^Pidfile=/var/run/radiusd/radsniff.pid$}) + .with_content(%r{^EnvironmentFile=/etc/sysconfig/radsniff$}) + .with_content(%r{^ExecStart=/usr/bin/radsniff -P /var/run/radiusd/radsniff.pid -d /etc/raddb \$RADSNIFF_OPTIONS$}) + .that_notifies('Service[radsniff]') + end + when 'Debian' + it do + is_expected.to contain_file('/etc/defaults/radsniff') + .with_content(%r{RADSNIFF_OPTIONS="radsniff cmd \\"line\\" options"}) + .that_notifies('Service[radsniff]') + .that_requires('Package[freeradius-utils]') + end + + it do + is_expected.to contain_systemd__unit_file('radsniff.service') + .with_content(%r{^Pidfile=/var/run/freeradius/radsniff.pid$}) + .with_content(%r{^EnvironmentFile=/etc/defaults/radsniff$}) + .with_content(%r{^ExecStart=/usr/bin/radsniff -P /var/run/freeradius/radsniff.pid -d /etc/freeradius \$RADSNIFF_OPTIONS$}) + .that_notifies('Service[radsniff]') + end + else + it do + is_expected.to compile.and_raise_error(%r{freeradius::radsniff requires envfile to be explicitly set on this OS}) + is_expected.to compile.and_raise_error(%r{freeradius::radsniff requires pidfile to be explicitly set on this OS}) + end + end + + context 'with envfile and pidfile set' do + let(:params) do + super().merge( + envfile: '/test/env/file', + pidfile: '/a/pid/file', + ) + end + + if os_facts[:osfamily] !~ %r{^RedHat|Debian$} + it do + is_expected.to contain_service('radsniff') + .with_ensure('running') + .with_enable(true) + end + end + + it do + is_expected.to contain_file('/test/env/file') + .with_content(%r{RADSNIFF_OPTIONS="radsniff cmd \\"line\\" options"}) + .that_notifies('Service[radsniff]') + .that_requires('Package[freeradius-utils]') + end + + it do + is_expected.to contain_systemd__unit_file('radsniff.service') + .with_content(%r{^Pidfile=/a/pid/file$}) + .with_content(%r{^EnvironmentFile=/test/env/file$}) + .with_content(%r{^ExecStart=/usr/bin/radsniff -P /a/pid/file -d .* \$RADSNIFF_OPTIONS$}) + .that_notifies('Service[radsniff]') + end + end + end + end +end diff --git a/spec/spec_helper_local.rb b/spec/spec_helper_local.rb index bac495b4..f66ce653 100644 --- a/spec/spec_helper_local.rb +++ b/spec/spec_helper_local.rb @@ -53,6 +53,20 @@ end end +# Same as above but enable utils +shared_context 'freeradius_with_utils' do + let(:pre_condition) do + [ + 'class freeradius { + $utils_support = true + } + include freeradius + + package { "freeradius-utils": }', + ] + end +end + # Some common dependencies for things based on names for redhat systems shared_context 'redhat_common_dependencies' do let(:pre_condition) do diff --git a/templates/radsniff.service.erb b/templates/radsniff.service.erb new file mode 100644 index 00000000..baf50dc8 --- /dev/null +++ b/templates/radsniff.service.erb @@ -0,0 +1,13 @@ +[Unit] +Description=Capture RADIUS statistics +After=syslog.target network.target +After=radiusd.target + +[Service] +Type=forking +Pidfile=<%=scope['::freeradius::radsniff::final_pidfile']%> +EnvironmentFile=<%=scope['::freeradius::radsniff::final_envfile']%> +ExecStart=/usr/bin/radsniff -P <%=scope['::freeradius::radsniff::final_pidfile']%> -d <%=scope['::freeradius::radsniff::fr_basepath']%> $RADSNIFF_OPTIONS + +[Install] +WantedBy=multi-user.target