diff options
22 files changed, 381 insertions, 352 deletions
| diff --git a/data/templates/accel-ppp/config_chap_secrets_radius.j2 b/data/templates/accel-ppp/config_chap_secrets_radius.j2 index bb820497b..a498d8186 100644 --- a/data/templates/accel-ppp/config_chap_secrets_radius.j2 +++ b/data/templates/accel-ppp/config_chap_secrets_radius.j2 @@ -7,6 +7,9 @@ verbose=1  {%     for server, options in authentication.radius.server.items() if not options.disable is vyos_defined %}  server={{ server }},{{ options.key }},auth-port={{ options.port }},acct-port={{ options.acct_port }},req-limit=0,fail-time={{ options.fail_time }}  {%     endfor %} +{%     if authentication.radius.accounting_interim_interval is vyos_defined %} +acct-interim-interval={{ authentication.radius.accounting_interim_interval }} +{%     endif %}  {%     if authentication.radius.acct_interim_jitter is vyos_defined %}  acct-interim-jitter={{ authentication.radius.acct_interim_jitter }}  {%     endif %} diff --git a/data/templates/rsyslog/logrotate.j2 b/data/templates/rsyslog/logrotate.j2 new file mode 100644 index 000000000..89d1a8a50 --- /dev/null +++ b/data/templates/rsyslog/logrotate.j2 @@ -0,0 +1,16 @@ +### Autogenerated by system-syslog.py ### +{% if file is vyos_defined %} +{%     for file_name, file_options in file.items() %} +/var/log/user/{{ file_name }} { +  missingok +  notifempty +  create +  rotate {{ file_options.archive.file }} +  size={{ file_options.archive.size | int // 1024 }}k +  postrotate +    invoke-rc.d rsyslog rotate > /dev/null +  endscript +} + +{%     endfor %} +{% endif %} diff --git a/data/templates/rsyslog/override.conf.j2 b/data/templates/rsyslog/override.conf.j2 new file mode 100644 index 000000000..5f6a87edf --- /dev/null +++ b/data/templates/rsyslog/override.conf.j2 @@ -0,0 +1,11 @@ +{% set vrf_command = 'ip vrf exec ' ~ vrf ~ ' ' if vrf is vyos_defined else '' %} +[Unit] +StartLimitIntervalSec=0 + +[Service] +ExecStart= +ExecStart={{ vrf_command }}/usr/sbin/rsyslogd -n -iNONE +Restart=always +RestartPreventExitStatus= +RestartSec=10 +RuntimeDirectoryPreserve=yes diff --git a/data/templates/rsyslog/rsyslog.conf.j2 b/data/templates/rsyslog/rsyslog.conf.j2 new file mode 100644 index 000000000..0460ae5f0 --- /dev/null +++ b/data/templates/rsyslog/rsyslog.conf.j2 @@ -0,0 +1,71 @@ +### Autogenerated by system-syslog.py ### + +{% if global.marker is vyos_defined %} +$ModLoad immark +{%     if global.marker.interval is vyos_defined %} +$MarkMessagePeriod {{ global.marker.interval }} +{%     endif %} +{% endif %} +{% if global.preserve_fqdn is vyos_defined %} +$PreserveFQDN on +{% endif %} + +# We always log to /var/log/messages +$outchannel global,/var/log/messages,262144,/usr/sbin/logrotate {{ logrotate }} +{% if global.facility is vyos_defined %} +{%     set tmp = [] %} +{%     for facility, facility_options in global.facility.items() %} +{%         set _ = tmp.append(facility.replace('all', '*') + '.' + facility_options.level) %} +{%     endfor %} +{{ tmp | join(';') }} :omfile:$global +{% endif %} + +{% if file is vyos_defined %} +# File based configuration section +{%     for file_name, file_options in file.items() %} +$outchannel {{ file_name }},/var/log/user/{{ file_name }},{{ file_options.archive.size }},/usr/sbin/logrotate {{ logrotate }} +{%         set tmp = [] %} +{%         for facility, facility_options in file_options.facility.items() %} +{%             set _ = tmp.append(facility.replace('all', '*') + '.' + facility_options.level) %} +{%         endfor %} +{{ tmp | join(';') }} :omfile:${{ file }} +{%     endfor %} +{% endif %} + +{% if console.facility is vyos_defined %} +# Console logging +{%     set tmp = [] %} +{%     for facility, facility_options in console.facility.items() %} +{%         set _ = tmp.append(facility.replace('all', '*') + '.' + facility_options.level) %} +{%     endfor %} +{{ tmp | join(';') }} /dev/console +{% endif %} + +{% if host is vyos_defined %} +# Remote logging +{%     for host_name, host_options in host.items() %} +{%         set tmp = [] %} +{%         for facility, facility_options in host_options.facility.items() %} +{%             set _ = tmp.append(facility.replace('all', '*') + '.' + facility_options.level) %} +{%         endfor %} +{%         if host_options.protocol is vyos_defined('tcp') %} +{%             if host_options.oct_count is vyos_defined %} +{{ tmp | join(';') }} @@(o){{ host_name | bracketize_ipv6 }}:{{ host_options.port }};RSYSLOG_SyslogProtocol23Format +{%             else %} +{{ tmp | join(';') }} @@{{ host_name | bracketize_ipv6 }}:{{ host_options.port }} +{%             endif %} +{%         else %} +{{ tmp | join(';') }} @{{ host_name | bracketize_ipv6 }}:{{ host_options.port }}{{ ';RSYSLOG_SyslogProtocol23Format' if host_options.format.octet_counted is vyos_defined }} +{%         endif %} +{%     endfor %} +{% endif %} + +{% if user is defined and user is not none %} +# Log to user terminal +{%     for username, user_options in user.items() %} +{%         for facility, facility_options in user_options.facility.items() %} +{%             set _ = tmp.append(facility.replace('all', '*') + '.' + facility_options.level) %} +{%         endfor %} +{{ tmp | join(';') }} :omusrmsg:{{ username }} +{%     endfor %} +{% endif %} diff --git a/data/templates/syslog/logrotate.j2 b/data/templates/syslog/logrotate.j2 deleted file mode 100644 index c1b951e8b..000000000 --- a/data/templates/syslog/logrotate.j2 +++ /dev/null @@ -1,11 +0,0 @@ -{{ config_render['log-file'] }} { -  missingok -  notifempty -  create -  rotate {{ config_render['max-files'] }} -  size={{ config_render['max-size'] // 1024 }}k -  postrotate -    invoke-rc.d rsyslog rotate > /dev/null -  endscript -} - diff --git a/data/templates/syslog/rsyslog.conf.j2 b/data/templates/syslog/rsyslog.conf.j2 deleted file mode 100644 index abe880283..000000000 --- a/data/templates/syslog/rsyslog.conf.j2 +++ /dev/null @@ -1,58 +0,0 @@ -## generated by syslog.py ## -## file based logging -{% if files['global']['marker'] %} -$ModLoad immark -{%     if files['global']['marker-interval'] %} -$MarkMessagePeriod {{ files['global']['marker-interval'] }} -{%     endif %} -{% endif %} -{% if files['global']['preserver_fqdn'] %} -$PreserveFQDN on -{% endif %} -{% for file, file_options in files.items() %} -{%     if file_options['max-size'] is vyos_defined %} -$outchannel {{ file }},{{ file_options['log-file'] }},{{ file_options['max-size'] }},{{ file_options['action-on-max-size'] }} -{%     else %} -$outchannel {{ file }},{{ file_options['log-file'] }} -{%     endif %} -{{ file_options['selectors'] }} :omfile:${{ file }} -{% endfor %} -{% if console is defined and console is not none %} -## console logging -{%     for con, con_options in console.items() %} -{{ con_options['selectors'] }} /dev/console -{%     endfor %} -{% endif %} -{% if hosts is defined and hosts is not none %} -## remote logging -{%     for host, host_options in hosts.items() %} -{%         if host_options.proto == 'tcp' %} -{%             if host_options.port is defined %} -{%                 if host_options.oct_count is defined %} -{{ host_options.selectors }} @@(o){{ host | bracketize_ipv6 }}:{{ host_options.port }};RSYSLOG_SyslogProtocol23Format -{%                 else %} -{{ host_options.selectors }} @@{{ host | bracketize_ipv6 }}:{{ host_options.port }} -{%                 endif %} -{%             else %} -{{ host_options.selectors }} @@{{ host | bracketize_ipv6 }} -{%             endif %} -{%         elif host_options.proto == 'udp' %} -{%             if host_options.port is defined %} -{{ host_options.selectors }} @{{ host | bracketize_ipv6 }}:{{ host_options.port }}{{ ';RSYSLOG_SyslogProtocol23Format' if host_options.oct_count is sameas true }} -{%             else %} -{{ host_options.selectors }} @{{ host | bracketize_ipv6 }} -{%             endif %} -{%         else %} -{%             if host_options['port'] %} -{{ host_options.selectors }} @{{ host | bracketize_ipv6 }}:{{ host_options.port }} -{%             else %} -{{ host_options.selectors }} @{{ host | bracketize_ipv6 }} -{%             endif %} -{%         endif %} -{%     endfor %} -{% endif %} -{% if user is defined and user is not none %} -{%     for username, user_options in user.items() %} -{{ user_options.selectors }} :omusrmsg:{{ username }} -{%     endfor %} -{% endif %} diff --git a/debian/vyos-1x.install b/debian/vyos-1x.install index 98d1bc0cd..2b04f173b 100644 --- a/debian/vyos-1x.install +++ b/debian/vyos-1x.install @@ -6,7 +6,7 @@ etc/netplug  etc/opennhrp  etc/modprobe.d  etc/ppp -etc/rsyslog.d +etc/rsyslog.conf  etc/securetty  etc/security  etc/sudoers.d diff --git a/debian/vyos-1x.preinst b/debian/vyos-1x.preinst index 213a23d9e..58f24cb5a 100644 --- a/debian/vyos-1x.preinst +++ b/debian/vyos-1x.preinst @@ -3,3 +3,4 @@ dpkg-divert --package vyos-1x --add --rename /etc/security/capability.conf  dpkg-divert --package vyos-1x --add --rename /lib/systemd/system/lcdproc.service  dpkg-divert --package vyos-1x --add --rename /etc/logrotate.d/conntrackd  dpkg-divert --package vyos-1x --add --rename /usr/share/pam-configs/radius +dpkg-divert --package vyos-1x --add --rename /etc/rsyslog.conf diff --git a/interface-definitions/include/accel-ppp/radius-additions.xml.i b/interface-definitions/include/accel-ppp/radius-additions.xml.i index 15ff5165f..cdd0bf300 100644 --- a/interface-definitions/include/accel-ppp/radius-additions.xml.i +++ b/interface-definitions/include/accel-ppp/radius-additions.xml.i @@ -1,6 +1,19 @@  <!-- include start from accel-ppp/radius-additions.xml.i -->  <node name="radius">    <children> +    <leafNode name="accounting-interim-interval"> +      <properties> +        <help>Interval in seconds to send accounting information</help> +        <valueHelp> +          <format>u32:1-3600</format> +          <description>Interval in seconds to send accounting information</description> +        </valueHelp> +        <constraint> +          <validator name="numeric" argument="--range 1-3600"/> +        </constraint> +        <constraintErrorMessage>Interval value must be between 1 and 3600 seconds</constraintErrorMessage> +      </properties> +    </leafNode>      <leafNode name="acct-interim-jitter">        <properties>          <help>Maximum jitter value in seconds to be applied to accounting information interval</help> diff --git a/interface-definitions/include/snmp/protocol.xml.i b/interface-definitions/include/protocol-tcp-udp.xml.i index d7e6752ad..d7e6752ad 100644 --- a/interface-definitions/include/snmp/protocol.xml.i +++ b/interface-definitions/include/protocol-tcp-udp.xml.i diff --git a/interface-definitions/include/syslog-facility.xml.i b/interface-definitions/include/syslog-facility.xml.i index 57067ece2..e6138a122 100644 --- a/interface-definitions/include/syslog-facility.xml.i +++ b/interface-definitions/include/syslog-facility.xml.i @@ -3,10 +3,10 @@    <properties>      <help>Facility for logging</help>      <completionHelp> -      <list>auth authpriv cron daemon kern lpr mail mark news protocols security syslog user uucp local0 local1 local2 local3 local4 local5 local6 local7 all</list> +      <list>auth authpriv cron daemon kern lpr mail mark news syslog user uucp local0 local1 local2 local3 local4 local5 local6 local7 all</list>      </completionHelp>      <constraint> -      <regex>(auth|authpriv|cron|daemon|kern|lpr|mail|mark|news|protocols|security|syslog|user|uucp|local0|local1|local2|local3|local4|local5|local6|local7|all)</regex> +      <regex>(auth|authpriv|cron|daemon|kern|lpr|mail|mark|news|syslog|user|uucp|local0|local1|local2|local3|local4|local5|local6|local7|all)</regex>      </constraint>      <constraintErrorMessage>Invalid facility type</constraintErrorMessage>      <valueHelp> @@ -50,14 +50,6 @@        <description>USENET subsystem</description>      </valueHelp>      <valueHelp> -      <format>protocols</format> -      <description>depricated will be set to local7</description> -    </valueHelp> -    <valueHelp> -      <format>security</format> -      <description>depricated will be set to auth</description> -    </valueHelp> -    <valueHelp>        <format>syslog</format>        <description>Authentication and authorization</description>      </valueHelp> @@ -109,10 +101,6 @@          <completionHelp>            <list>emerg alert crit err warning notice info debug all</list>          </completionHelp> -        <constraint> -          <regex>(emerg|alert|crit|err|warning|notice|info|debug|all)</regex> -        </constraint> -        <constraintErrorMessage>Invalid loglevel</constraintErrorMessage>          <valueHelp>            <format>emerg</format>            <description>Emergency messages</description> @@ -149,7 +137,12 @@            <format>all</format>            <description>Log everything</description>          </valueHelp> +        <constraint> +          <regex>(emerg|alert|crit|err|warning|notice|info|debug|all)</regex> +        </constraint> +        <constraintErrorMessage>Invalid loglevel</constraintErrorMessage>        </properties> +      <defaultValue>err</defaultValue>      </leafNode>    </children>  </tagNode> diff --git a/interface-definitions/include/version/firewall-version.xml.i b/interface-definitions/include/version/firewall-version.xml.i index bc04f8d51..c32484542 100644 --- a/interface-definitions/include/version/firewall-version.xml.i +++ b/interface-definitions/include/version/firewall-version.xml.i @@ -1,3 +1,3 @@  <!-- include start from include/version/firewall-version.xml.i --> -<syntaxVersion component='firewall' version='9'></syntaxVersion> +<syntaxVersion component='firewall' version='10'></syntaxVersion>  <!-- include end --> diff --git a/interface-definitions/include/version/system-version.xml.i b/interface-definitions/include/version/system-version.xml.i index b7650c782..73df8bd8e 100644 --- a/interface-definitions/include/version/system-version.xml.i +++ b/interface-definitions/include/version/system-version.xml.i @@ -1,3 +1,3 @@  <!-- include start from include/version/system-version.xml.i --> -<syntaxVersion component='system' version='25'></syntaxVersion> +<syntaxVersion component='system' version='26'></syntaxVersion>  <!-- include end --> diff --git a/interface-definitions/snmp.xml.in b/interface-definitions/snmp.xml.in index 559e09388..6527cabd6 100644 --- a/interface-definitions/snmp.xml.in +++ b/interface-definitions/snmp.xml.in @@ -143,7 +143,7 @@                <multi/>              </properties>            </leafNode> -          #include <include/snmp/protocol.xml.i> +          #include <include/protocol-tcp-udp.xml.i>            <leafNode name="smux-peer">              <properties>                <help>Register a subtree for SMUX-based processing</help> @@ -327,7 +327,7 @@                        #include <include/snmp/privacy-type.xml.i>                      </children>                    </node> -                  #include <include/snmp/protocol.xml.i> +                  #include <include/protocol-tcp-udp.xml.i>                    <leafNode name="type">                      <properties>                        <help>Specifies the type of notification between inform and trap</help> diff --git a/interface-definitions/system-syslog.xml.in b/interface-definitions/system-syslog.xml.in index 4a2adfd5f..cd5c514a8 100644 --- a/interface-definitions/system-syslog.xml.in +++ b/interface-definitions/system-syslog.xml.in @@ -50,6 +50,10 @@              </properties>              <children>                #include <include/port-number.xml.i> +              <leafNode name="port"> +                <defaultValue>514</defaultValue> +              </leafNode> +              #include <include/protocol-tcp-udp.xml.i>                #include <include/syslog-facility.xml.i>                <node name="format">                  <properties> @@ -79,11 +83,12 @@                  <children>                    <leafNode name="interval">                      <properties> -                      <help>time interval how often a mark message is being sent in seconds (default: 1200)</help> +                      <help>time interval how often a mark message is being sent in seconds</help>                        <constraint>                          <validator name="numeric" argument="--positive"/>                        </constraint>                      </properties> +                    <defaultValue>1200</defaultValue>                    </leafNode>                  </children>                </node> @@ -111,21 +116,23 @@                  <children>                    <leafNode name="file">                      <properties> -                      <help>Number of saved files (default is 5)</help> +                      <help>Number of saved files</help>                        <constraint>                          <regex>[0-9]+</regex>                        </constraint>                        <constraintErrorMessage>illegal characters in number of files</constraintErrorMessage>                      </properties> +                    <defaultValue>5</defaultValue>                    </leafNode>                    <leafNode name="size">                      <properties> -                      <help>Size of log files (in kbytes, default is 256)</help> +                      <help>Size of log files in kbytes</help>                        <constraint>                          <regex>[0-9]+</regex>                        </constraint>                        <constraintErrorMessage>illegal characters in size</constraintErrorMessage>                      </properties> +                    <defaultValue>256</defaultValue>                    </leafNode>                  </children>                </node> @@ -140,6 +147,7 @@                #include <include/syslog-facility.xml.i>              </children>            </node> +          #include <include/interface/vrf.xml.i>          </children>        </node>      </children> diff --git a/smoketest/configs/basic-vyos b/smoketest/configs/basic-vyos index 23186b9b8..033c1a518 100644 --- a/smoketest/configs/basic-vyos +++ b/smoketest/configs/basic-vyos @@ -127,14 +127,40 @@ system {      }      name-server 192.168.0.1      syslog { -        global { -            archive { -                file 5 -                size 512 +        console { +            facility all { +                level emerg +            } +            facility mail { +                level info              } +        } +        global {              facility all {                  level info              } +            facility protocols { +                level debug +            } +            facility security { +                level info +            } +            preserve-fqdn +        } +        host syslog.vyos.net { +            facility local7 { +                level notice +            } +            facility protocols { +                level alert +            } +            facility security { +                level warning +            } +            format { +                octet-counted +            } +            port 8000          }      }      time-zone Europe/Berlin diff --git a/smoketest/scripts/cli/test_service_pppoe-server.py b/smoketest/scripts/cli/test_service_pppoe-server.py index 4f9181704..bb6a1c1cd 100755 --- a/smoketest/scripts/cli/test_service_pppoe-server.py +++ b/smoketest/scripts/cli/test_service_pppoe-server.py @@ -1,6 +1,6 @@  #!/usr/bin/env python3  # -# Copyright (C) 2022 VyOS maintainers and contributors +# Copyright (C) 2022-2023 VyOS maintainers and contributors  #  # This program is free software; you can redistribute it and/or modify  # it under the terms of the GNU General Public License version 2 or later as @@ -243,9 +243,11 @@ class TestServicePPPoEServer(BasicAccelPPPTest.TestCase):      def test_accel_radius_authentication(self):          radius_called_sid = 'ifname:mac'          radius_acct_interim_jitter = '9' +        radius_acct_interim_interval = '60'          self.set(['authentication', 'radius', 'called-sid-format', radius_called_sid])          self.set(['authentication', 'radius', 'acct-interim-jitter', radius_acct_interim_jitter]) +        self.set(['authentication', 'radius', 'accounting-interim-interval', radius_acct_interim_interval])          # run common tests          super().test_accel_radius_authentication() @@ -257,6 +259,7 @@ class TestServicePPPoEServer(BasicAccelPPPTest.TestCase):          # Validate configuration          self.assertEqual(conf['pppoe']['called-sid'], radius_called_sid)          self.assertEqual(conf['radius']['acct-interim-jitter'], radius_acct_interim_jitter) +        self.assertEqual(conf['radius']['acct-interim-interval'], radius_acct_interim_interval)      def test_pppoe_server_vlan(self): diff --git a/src/conf_mode/service_pppoe-server.py b/src/conf_mode/service_pppoe-server.py index 600ba4e92..adeefaa37 100755 --- a/src/conf_mode/service_pppoe-server.py +++ b/src/conf_mode/service_pppoe-server.py @@ -1,6 +1,6 @@  #!/usr/bin/env python3  # -# Copyright (C) 2018-2022 VyOS maintainers and contributors +# Copyright (C) 2018-2023 VyOS maintainers and contributors  #  # This program is free software; you can redistribute it and/or modify  # it under the terms of the GNU General Public License version 2 or later as @@ -71,8 +71,9 @@ def verify(pppoe):      # local ippool and gateway settings config checks      if not (dict_search('client_ip_pool.subnet', pppoe) or +           (dict_search('client_ip_pool.name', pppoe) or             (dict_search('client_ip_pool.start', pppoe) and -            dict_search('client_ip_pool.stop', pppoe))): +            dict_search('client_ip_pool.stop', pppoe)))):          print('Warning: No PPPoE client pool defined')      if dict_search('authentication.radius.dynamic_author.server', pppoe): diff --git a/src/conf_mode/system-syslog.py b/src/conf_mode/system-syslog.py index 20132456c..e646fb0ae 100755 --- a/src/conf_mode/system-syslog.py +++ b/src/conf_mode/system-syslog.py @@ -1,6 +1,6 @@  #!/usr/bin/env python3  # -# Copyright (C) 2018-2020 VyOS maintainers and contributors +# Copyright (C) 2018-2023 VyOS maintainers and contributors  #  # This program is free software; you can redistribute it and/or modify  # it under the terms of the GNU General Public License version 2 or later as @@ -15,253 +15,129 @@  # along with this program.  If not, see <http://www.gnu.org/licenses/>.  import os -import re -from pathlib import Path  from sys import exit  from vyos.config import Config -from vyos import ConfigError -from vyos.util import run +from vyos.configdict import dict_merge +from vyos.configdict import is_node_changed +from vyos.configverify import verify_vrf +from vyos.util import call  from vyos.template import render - +from vyos.xml import defaults +from vyos import ConfigError  from vyos import airbag  airbag.enable() +rsyslog_conf = '/etc/rsyslog.d/00-vyos.conf' +logrotate_conf = '/etc/logrotate.d/vyos-rsyslog' +systemd_override = r'/run/systemd/system/rsyslog.service.d/override.conf' +  def get_config(config=None):      if config: -        c = config +        conf = config      else: -        c = Config() -    if not c.exists('system syslog'): +        conf = Config() +    base = ['system', 'syslog'] +    if not conf.exists(base):          return None -    c.set_level('system syslog') - -    config_data = { -        'files': {}, -      'console': {}, -      'hosts': {}, -      'user': {} -    } - -    # -    # /etc/rsyslog.d/vyos-rsyslog.conf -    # 'set system syslog global' -    # -    config_data['files'].update( -        { -            'global': { -                'log-file': '/var/log/messages', -                'selectors': '*.notice;local7.debug', -                'max-files': '5', -                'preserver_fqdn': False -            } -        } -    ) - -    if c.exists('global marker'): -        config_data['files']['global']['marker'] = True -        if c.exists('global marker interval'): -            config_data['files']['global'][ -                'marker-interval'] = c.return_value('global marker interval') -    if c.exists('global facility'): -        config_data['files']['global'][ -            'selectors'] = generate_selectors(c, 'global facility') -    if c.exists('global archive size'): -        config_data['files']['global']['max-size'] = int( -            c.return_value('global archive size')) * 1024 -    if c.exists('global archive file'): -        config_data['files']['global'][ -            'max-files'] = c.return_value('global archive file') -    if c.exists('global preserve-fqdn'): -        config_data['files']['global']['preserver_fqdn'] = True - -    # -    # set system syslog file -    # - -    if c.exists('file'): -        filenames = c.list_nodes('file') -        for filename in filenames: -            config_data['files'].update( -                { -                    filename: { -                        'log-file': '/var/log/user/' + filename, -                        'max-files': '5', -                        'action-on-max-size': '/usr/sbin/logrotate /etc/logrotate.d/vyos-rsyslog-generated-' + filename, -                        'selectors': '*.err', -                        'max-size': 262144 -                    } -                } -            ) - -            if c.exists('file ' + filename + ' facility'): -                config_data['files'][filename]['selectors'] = generate_selectors( -                    c, 'file ' + filename + ' facility') -            if c.exists('file ' + filename + ' archive size'): -                config_data['files'][filename]['max-size'] = int( -                    c.return_value('file ' + filename + ' archive size')) * 1024 -            if c.exists('file ' + filename + ' archive files'): -                config_data['files'][filename]['max-files'] = c.return_value( -                    'file ' + filename + ' archive files') - -    # set system syslog console -    if c.exists('console'): -        config_data['console'] = { -            '/dev/console': { -                'selectors': '*.err' -            } -        } - -    for f in c.list_nodes('console facility'): -        if c.exists('console facility ' + f + ' level'): -            config_data['console'] = { -                '/dev/console': { -                    'selectors': generate_selectors(c, 'console facility') -                } -            } -    # set system syslog host -    if c.exists('host'): -        rhosts = c.list_nodes('host') -        proto = 'udp' -        for rhost in rhosts: -            for fac in c.list_nodes('host ' + rhost + ' facility'): -                if c.exists('host ' + rhost + ' facility ' + fac + ' protocol'): -                    proto = c.return_value( -                        'host ' + rhost + ' facility ' + fac + ' protocol') -                else: -                    proto = 'udp' - -            config_data['hosts'].update( -                { -                    rhost: { -                        'selectors': generate_selectors(c, 'host ' + rhost + ' facility'), -                        'proto': proto -                    } -                } -            ) -            if c.exists('host ' + rhost + ' port'): -                config_data['hosts'][rhost][ -                    'port'] = c.return_value(['host', rhost, 'port']) - -            # set system syslog host x.x.x.x format octet-counted -            if c.exists('host ' + rhost + ' format octet-counted'): -                config_data['hosts'][rhost]['oct_count'] = True -            else: -                config_data['hosts'][rhost]['oct_count'] = False - -    # set system syslog user -    if c.exists('user'): -        usrs = c.list_nodes('user') -        for usr in usrs: -            config_data['user'].update( -                { -                    usr: { -                        'selectors': generate_selectors(c, 'user ' + usr + ' facility') -                    } -                } -            ) - -    return config_data - - -def generate_selectors(c, config_node): -# protocols and security are being mapped here -# for backward compatibility with old configs -# security and protocol mappings can be removed later -    nodes = c.list_nodes(config_node) -    selectors = "" -    for node in nodes: -        lvl = c.return_value(config_node + ' ' + node + ' level') -        if lvl == None: -            lvl = "err" -        if lvl == 'all': -            lvl = '*' -        if node == 'all' and node != nodes[-1]: -            selectors += "*." + lvl + ";" -        elif node == 'all': -            selectors += "*." + lvl -        elif node != nodes[-1]: -            if node == 'protocols': -                node = 'local7' -            if node == 'security': -                node = 'auth' -            selectors += node + "." + lvl + ";" -        else: -            if node == 'protocols': -                node = 'local7' -            if node == 'security': -                node = 'auth' -            selectors += node + "." + lvl -    return selectors - - -def generate(c): -    if c == None: +    syslog = conf.get_config_dict(base, key_mangling=('-', '_'), +                                  get_first_key=True, no_tag_node_value_mangle=True) + +    syslog.update({ 'logrotate' : logrotate_conf }) +    tmp = is_node_changed(conf, base + ['vrf']) +    if tmp: syslog.update({'restart_required': {}}) + +    # We have gathered the dict representation of the CLI, but there are default +    # options which we need to update into the dictionary retrived. +    default_values = defaults(base) +    # XXX: some syslog default values can not be merged here (originating from +    # a tagNode - remove and add them later per individual tagNode instance +    if 'console' in default_values: +        del default_values['console'] +    for entity in ['global', 'user', 'host', 'file']: +        if entity in default_values: +            del default_values[entity] + +    syslog = dict_merge(default_values, syslog) + +    # XXX: add defaults for "console" tree +    if 'console' in syslog and 'facility' in syslog['console']: +        default_values = defaults(base + ['console', 'facility']) +        for facility in syslog['console']['facility']: +            syslog['console']['facility'][facility] = dict_merge(default_values, +                                                                syslog['console']['facility'][facility]) + +    # XXX: add defaults for "host" tree +    if 'host' in syslog: +        default_values_host = defaults(base + ['host']) +        if 'facility' in default_values_host: +            del default_values_host['facility'] +        default_values_facility = defaults(base + ['host', 'facility']) + +        for host, host_config in syslog['host'].items(): +            syslog['host'][host] = dict_merge(default_values_host, syslog['host'][host]) +            if 'facility' in host_config: +                for facility in host_config['facility']: +                    syslog['host'][host]['facility'][facility] = dict_merge(default_values_facility, +                                                                        syslog['host'][host]['facility'][facility]) + +    # XXX: add defaults for "user" tree +    if 'user' in syslog: +        default_values = defaults(base + ['user', 'facility']) +        for user, user_config in syslog['user'].items(): +            if 'facility' in user_config: +                for facility in user_config['facility']: +                    syslog['user'][user]['facility'][facility] = dict_merge(default_values, +                                                                        syslog['user'][user]['facility'][facility]) + +    # XXX: add defaults for "file" tree +    if 'file' in syslog: +        default_values = defaults(base + ['file']) +        for file, file_config in syslog['file'].items(): +            for facility in file_config['facility']: +                syslog['file'][file]['facility'][facility] = dict_merge(default_values, +                                                                        syslog['file'][file]['facility'][facility]) + +    return syslog + +def verify(syslog): +    if not syslog:          return None -    conf = '/etc/rsyslog.d/vyos-rsyslog.conf' -    render(conf, 'syslog/rsyslog.conf.j2', c) - -    # cleanup current logrotate config files -    logrotate_files = Path('/etc/logrotate.d/').glob('vyos-rsyslog-generated-*') -    for file in logrotate_files: -        file.unlink() +    verify_vrf(syslog) -    # eventually write for each file its own logrotate file, since size is -    # defined it shouldn't matter -    for filename, fileconfig in c.get('files', {}).items(): -        if fileconfig['log-file'].startswith('/var/log/user/'): -            conf = '/etc/logrotate.d/vyos-rsyslog-generated-' + filename -            render(conf, 'syslog/logrotate.j2', { 'config_render': fileconfig }) +def generate(syslog): +    if not syslog: +        if os.path.exists(rsyslog_conf): +            os.path.unlink(rsyslog_conf) +        if os.path.exists(logrotate_conf): +            os.path.unlink(logrotate_conf) - -def verify(c): -    if c == None:          return None -    # may be obsolete -    # /etc/rsyslog.conf is generated somewhere and copied over the original (exists in /opt/vyatta/etc/rsyslog.conf) -    # it interferes with the global logging, to make sure we are using a single base, template is enforced here -    # -    if not os.path.islink('/etc/rsyslog.conf'): -        os.remove('/etc/rsyslog.conf') -        os.symlink( -            '/usr/share/vyos/templates/rsyslog/rsyslog.conf', '/etc/rsyslog.conf') +    render(rsyslog_conf, 'rsyslog/rsyslog.conf.j2', syslog) +    render(systemd_override, 'rsyslog/override.conf.j2', syslog) +    render(logrotate_conf, 'rsyslog/logrotate.j2', syslog) -    # /var/log/vyos-rsyslog were the old files, we may want to clean those up, but currently there -    # is a chance that someone still needs it, so I don't automatically remove -    # them -    # +    # Reload systemd manager configuration +    call('systemctl daemon-reload') +    return None -    if c == None: +def apply(syslog): +    systemd_service = 'syslog.service' +    if not syslog: +        call(f'systemctl stop {systemd_service}')          return None -    fac = [ -        '*', 'auth', 'authpriv', 'cron', 'daemon', 'kern', 'lpr', 'mail', 'mark', 'news', 'protocols', 'security', -          'syslog', 'user', 'uucp', 'local0', 'local1', 'local2', 'local3', 'local4', 'local5', 'local6', 'local7'] -    lvl = ['emerg', 'alert', 'crit', 'err', -           'warning', 'notice', 'info', 'debug', '*'] - -    for conf in c: -        if c[conf]: -            for item in c[conf]: -                for s in c[conf][item]['selectors'].split(";"): -                    f = re.sub("\..*$", "", s) -                    if f not in fac: -                        raise ConfigError( -                            'Invalid facility ' + s + ' set in ' + conf + ' ' + item) -                    l = re.sub("^.+\.", "", s) -                    if l not in lvl: -                        raise ConfigError( -                            'Invalid logging level ' + s + ' set in ' + conf + ' ' + item) - +    # we need to restart the service if e.g. the VRF name changed +    systemd_action = 'reload-or-restart' +    if 'restart_required' in syslog: +        systemd_action = 'restart' -def apply(c): -    if not c: -        return run('systemctl stop syslog.service') -    return run('systemctl restart syslog.service') +    call(f'systemctl {systemd_action} {systemd_service}') +    return None  if __name__ == '__main__':      try: diff --git a/data/templates/rsyslog/rsyslog.conf b/src/etc/rsyslog.conf index ab60fc0f0..c28e9b537 100644 --- a/data/templates/rsyslog/rsyslog.conf +++ b/src/etc/rsyslog.conf @@ -1,6 +1,3 @@ -#  /etc/rsyslog.conf    Configuration file for rsyslog. -# -  #################  #### MODULES ####  ################# @@ -14,22 +11,30 @@ $SystemLogSocketName /run/systemd/journal/syslog  $KLogPath /proc/kmsg -# provides UDP syslog reception -#$ModLoad imudp -#$UDPServerRun 514 - -# provides TCP syslog reception -#$ModLoad imtcp -#$InputTCPServerRun 514 -  ###########################  #### GLOBAL DIRECTIVES ####  ########################### -# +# The lines below cause all listed daemons/processes to be logged into +# /var/log/auth.log, then drops the message so it does not also go to the +# regular syslog so that messages are not duplicated + +$outchannel auth_log,/var/log/auth.log +if  $programname == 'CRON' or +    $programname == 'sudo' or +    $programname == 'su' +    then :omfile:$auth_log + +if $programname == 'CRON' or +    $programname == 'sudo' or +    $programname == 'su' +    then stop +  # Use traditional timestamp format.  # To enable high precision timestamps, comment out the following line. -# +# A modern-style logfile format similar to TraditionalFileFormat, buth with high-precision timestamps and timezone information +#$ActionFileDefaultTemplate RSYSLOG_FileFormat +# The "old style" default log file format with low-precision timestamps  $ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat  # Filter duplicated messages @@ -44,6 +49,11 @@ $FileCreateMode 0640  $DirCreateMode 0755  $Umask 0022 +# +# Stop excessive logging of sudo +# +:msg, contains, " pam_unix(sudo:session): session opened for user root(uid=0) by" ~ +:msg, contains, "pam_unix(sudo:session): session closed for user root" ~  #  # Include all config files in /etc/rsyslog.d/ @@ -54,6 +64,4 @@ $IncludeConfig /etc/rsyslog.d/*.conf  #### RULES ####  ###############  # Emergencies are sent to everybody logged in. - -*.emerg                         :omusrmsg:* - +*.emerg                         :omusrmsg:*
\ No newline at end of file diff --git a/src/etc/rsyslog.d/01-auth.conf b/src/etc/rsyslog.d/01-auth.conf deleted file mode 100644 index cc64099d6..000000000 --- a/src/etc/rsyslog.d/01-auth.conf +++ /dev/null @@ -1,14 +0,0 @@ -# The lines below cause all listed daemons/processes to be logged into -# /var/log/auth.log, then drops the message so it does not also go to the -# regular syslog so that messages are not duplicated - -$outchannel auth_log,/var/log/auth.log -if  $programname == 'CRON' or -    $programname == 'sudo' or -    $programname == 'su' -    then :omfile:$auth_log - -if $programname == 'CRON' or -    $programname == 'sudo' or -    $programname == 'su' -    then stop diff --git a/src/migration-scripts/system/25-to-26 b/src/migration-scripts/system/25-to-26 new file mode 100755 index 000000000..615274430 --- /dev/null +++ b/src/migration-scripts/system/25-to-26 @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2023 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program.  If not, see <http://www.gnu.org/licenses/>. +# +# syslog: migrate deprecated CLI options +#         - protocols -> local7 +#         - security -> auth + +from sys import exit, argv +from vyos.configtree import ConfigTree + +if (len(argv) < 1): +    print("Must specify file name!") +    exit(1) + +file_name = argv[1] +with open(file_name, 'r') as f: +    config_file = f.read() + +base = ['system', 'syslog'] +config = ConfigTree(config_file) + +if not config.exists(base): +    exit(0) + +def rename_facilities(config, base_tree, facility, facility_new) -> None: +    if config.exists(base + [base_tree, 'facility', facility]): +        # do not overwrite already existing replacement facility +        if not config.exists(base + [base_tree, 'facility', facility_new]): +            config.rename(base + [base_tree, 'facility', facility], facility_new) +        else: +            # delete old duplicate facility config +            config.delete(base + [base_tree, 'facility', facility]) + +# +# Rename protocols and securityy facility to common ones +# +replace = { +    'protocols' : 'local7', +    'security' : 'auth' +} +for facility, facility_new in replace.items(): +    rename_facilities(config, 'console', facility, facility_new) +    rename_facilities(config, 'global', facility, facility_new) + +    if config.exists(base + ['host']): +        for host in config.list_nodes(base + ['host']): +            rename_facilities(config, f'host {host}', facility, facility_new) + +# +# It makes no sense to configure udp/tcp transport per individual facility +# +if config.exists(base + ['host']): +    for host in config.list_nodes(base + ['host']): +        protocol = None +        for facility in config.list_nodes(base + ['host', host, 'facility']): +            tmp_path = base + ['host', host, 'facility', facility, 'protocol'] +            if config.exists(tmp_path): +                # We can only change the first one +                if protocol == None: +                    protocol = config.return_value(tmp_path) +                    config.set(base + ['host', host, 'protocol'], value=protocol) +                config.delete(tmp_path) + +try: +    with open(file_name, 'w') as f: +        f.write(config.to_string()) +except OSError as e: +    print(f'Failed to save the modified config: {e}') +    exit(1) | 
