diff options
-rw-r--r-- | data/templates/rsyslog/rsyslog.conf.j2 | 10 | ||||
-rw-r--r-- | interface-definitions/system_syslog.xml.in | 14 | ||||
-rw-r--r-- | smoketest/config-tests/basic-syslog | 25 | ||||
-rw-r--r-- | smoketest/configs/basic-syslog | 70 | ||||
-rwxr-xr-x | smoketest/scripts/cli/test_system_syslog.py | 65 | ||||
-rwxr-xr-x | src/conf_mode/system_syslog.py | 46 | ||||
-rw-r--r-- | src/migration-scripts/system/28-to-29 | 7 |
7 files changed, 201 insertions, 36 deletions
diff --git a/data/templates/rsyslog/rsyslog.conf.j2 b/data/templates/rsyslog/rsyslog.conf.j2 index daaafa6d1..45742b073 100644 --- a/data/templates/rsyslog/rsyslog.conf.j2 +++ b/data/templates/rsyslog/rsyslog.conf.j2 @@ -106,12 +106,11 @@ if prifilt("{{ tmp | join(',') }}") then { template="SyslogProtocol23Format" {% endif %} TCP_Framing="{{ 'octed-counted' if remote_options.format.octet_counted is vyos_defined else 'traditional' }}" -{% if source_address is vyos_defined %} - # Sender IP address - Address="{{ source_address }}" +{% if remote_options.source_address is vyos_defined %} + Address="{{ remote_options.source_address }}" {% endif %} -{% if vrf is vyos_defined %} - Device="{{ vrf }}" +{% if remote_options.vrf is vyos_defined %} + Device="{{ remote_options.vrf }}" {% endif %} ) } @@ -121,3 +120,4 @@ if prifilt("{{ tmp | join(',') }}") then { # Include all configuration files in /etc/rsyslog.d/ include(file="/etc/rsyslog.d/*.conf") + diff --git a/interface-definitions/system_syslog.xml.in b/interface-definitions/system_syslog.xml.in index 0dbf5d497..91fb680e0 100644 --- a/interface-definitions/system_syslog.xml.in +++ b/interface-definitions/system_syslog.xml.in @@ -38,11 +38,6 @@ </valueHelp> </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> @@ -63,6 +58,13 @@ </leafNode> </children> </node> + #include <include/port-number.xml.i> + <leafNode name="port"> + <defaultValue>514</defaultValue> + </leafNode> + #include <include/protocol-tcp-udp.xml.i> + #include <include/source-address-ipv4-ipv6.xml.i> + #include <include/interface/vrf.xml.i> </children> </tagNode> <node name="local"> @@ -100,8 +102,6 @@ <valueless/> </properties> </leafNode> - #include <include/source-address-ipv4-ipv6.xml.i> - #include <include/interface/vrf.xml.i> </children> </node> </children> diff --git a/smoketest/config-tests/basic-syslog b/smoketest/config-tests/basic-syslog new file mode 100644 index 000000000..349d642fd --- /dev/null +++ b/smoketest/config-tests/basic-syslog @@ -0,0 +1,25 @@ +set interfaces ethernet eth0 duplex 'auto' +set interfaces ethernet eth0 speed 'auto' +set interfaces ethernet eth1 address '172.16.33.154/24' +set interfaces ethernet eth1 duplex 'auto' +set interfaces ethernet eth1 speed 'auto' +set interfaces ethernet eth1 vrf 'red' +set system console device ttyS0 speed '115200' +set system domain-name 'vyos-ci-test.net' +set system host-name 'vyos' +set system login user vyos authentication encrypted-password '$6$O5gJRlDYQpj$MtrCV9lxMnZPMbcxlU7.FI793MImNHznxGoMFgm3Q6QP3vfKJyOSRCt3Ka/GzFQyW1yZS4NS616NLHaIPPFHc0' +set system login user vyos authentication plaintext-password '' +set system syslog local facility all level 'info' +set system syslog local facility local7 level 'debug' +set system syslog marker interval '999' +set system syslog preserve-fqdn +set system syslog remote syslog01.vyos.net facility local7 level 'notice' +set system syslog remote syslog01.vyos.net port '8000' +set system syslog remote syslog01.vyos.net vrf 'red' +set system syslog remote syslog02.vyos.net facility all level 'debug' +set system syslog remote syslog02.vyos.net format include-timezone +set system syslog remote syslog02.vyos.net format octet-counted +set system syslog remote syslog02.vyos.net port '8001' +set system syslog remote syslog02.vyos.net protocol 'tcp' +set system syslog remote syslog02.vyos.net vrf 'red' +set vrf name red table '12321' diff --git a/smoketest/configs/basic-syslog b/smoketest/configs/basic-syslog new file mode 100644 index 000000000..9336b73bc --- /dev/null +++ b/smoketest/configs/basic-syslog @@ -0,0 +1,70 @@ +interfaces { + ethernet eth0 { + duplex "auto" + speed "auto" + } + ethernet eth1 { + address 172.16.33.154/24 + duplex auto + speed auto + vrf red + } +} +system { + console { + device ttyS0 { + speed 115200 + } + } + domain-name vyos-ci-test.net + host-name vyos + login { + user vyos { + authentication { + encrypted-password $6$O5gJRlDYQpj$MtrCV9lxMnZPMbcxlU7.FI793MImNHznxGoMFgm3Q6QP3vfKJyOSRCt3Ka/GzFQyW1yZS4NS616NLHaIPPFHc0 + plaintext-password "" + } + } + } + syslog { + global { + facility all { + level info + } + facility local7 { + level debug + } + marker { + interval 999 + } + preserve-fqdn + } + host syslog01.vyos.net { + facility local7 { + level notice + } + port 8000 + } + host syslog02.vyos.net { + facility all { + level debug + } + format { + include-timezone + octet-counted + } + protocol tcp + port 8001 + } + vrf red + } +} +vrf { + name red { + table 12321 + } +} + +// Warning: Do not remove the following line. +// vyos-config-version: "bgp@5:broadcast-relay@1:cluster@2:config-management@1:conntrack@5:conntrack-sync@2:container@2:dhcp-relay@2:dhcp-server@8:dhcpv6-server@1:dns-dynamic@4:dns-forwarding@4:firewall@15:flow-accounting@1:https@6:ids@1:interfaces@32:ipoe-server@3:ipsec@13:isis@3:l2tp@9:lldp@2:mdns@1:monitoring@1:nat@8:nat66@3:ntp@3:openconnect@3:ospf@2:pim@1:policy@8:pppoe-server@10:pptp@5:qos@2:quagga@11:reverse-proxy@1:rip@1:rpki@2:salt@1:snmp@3:ssh@2:sstp@6:system@27:vrf@3:vrrp@4:vyos-accel-ppp@2:wanloadbalance@3:webproxy@2" +// Release version: 1.4.0 diff --git a/smoketest/scripts/cli/test_system_syslog.py b/smoketest/scripts/cli/test_system_syslog.py index 5a09fab18..bf6d3134d 100755 --- a/smoketest/scripts/cli/test_system_syslog.py +++ b/smoketest/scripts/cli/test_system_syslog.py @@ -14,7 +14,6 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -import re import unittest from base_vyostest_shim import VyOSUnitTestSHIM @@ -47,6 +46,7 @@ class TestRSYSLOGService(VyOSUnitTestSHIM.TestCase): # ensure we can also run this test on a live system - so lets clean # out the current configuration :) cls.cli_delete(cls, base_path) + cls.cli_delete(cls, ['vrf']) def tearDown(self): # Check for running process @@ -204,5 +204,68 @@ class TestRSYSLOGService(VyOSUnitTestSHIM.TestCase): else: self.assertIn( ' TCP_Framing="traditional"', config) + def test_vrf_source_address(self): + rhosts = { + '169.254.0.10': { }, + '169.254.0.11': { + 'vrf': {'name' : 'red', 'table' : '12321'}, + 'source_address' : '169.254.0.11', + }, + '169.254.0.12': { + 'vrf': {'name' : 'green', 'table' : '12322'}, + 'source_address' : '169.254.0.12', + }, + '169.254.0.13': { + 'vrf': {'name' : 'blue', 'table' : '12323'}, + 'source_address' : '169.254.0.13', + }, + } + + for remote, remote_options in rhosts.items(): + remote_base = base_path + ['remote', remote] + self.cli_set(remote_base + ['facility', 'all']) + + vrf = None + if 'vrf' in remote_options: + vrf = remote_options['vrf']['name'] + self.cli_set(['vrf', 'name', vrf, 'table', remote_options['vrf']['table']]) + self.cli_set(remote_base + ['vrf', vrf]) + + if 'source_address' in remote_options: + source_address = remote_options['source_address'] + self.cli_set(remote_base + ['source-address', source_address]) + + idx = source_address.split('.')[-1] + self.cli_set(['interfaces', 'dummy', f'dum{idx}', 'address', f'{source_address}/32']) + if vrf: + self.cli_set(['interfaces', 'dummy', f'dum{idx}', 'vrf', vrf]) + + + self.cli_commit() + config = read_file(RSYSLOG_CONF) + + for remote, remote_options in rhosts.items(): + config = get_config(f'# Remote syslog to {remote}') + + self.assertIn(f'target="{remote}"', config) + if 'vrf' in remote_options: + vrf = remote_options['vrf']['name'] + self.assertIn(f'Device="{vrf}"', config) + + if 'source_address' in remote_options: + source_address = remote_options['source_address'] + self.assertIn(f'Address="{source_address}"', config) + + # Cleanup VRF/Dummy interfaces + for remote, remote_options in rhosts.items(): + if 'vrf' in remote_options: + vrf = remote_options['vrf']['name'] + self.cli_delete(['vrf', 'name', vrf]) + + if 'source_address' in remote_options: + source_address = remote_options['source_address'] + idx = source_address.split('.')[-1] + self.cli_delete(['interfaces', 'dummy', f'dum{idx}']) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/src/conf_mode/system_syslog.py b/src/conf_mode/system_syslog.py index f27c27e0b..00c571ea9 100755 --- a/src/conf_mode/system_syslog.py +++ b/src/conf_mode/system_syslog.py @@ -20,11 +20,12 @@ from sys import exit from vyos.base import Warning from vyos.config import Config -from vyos.configdict import is_node_changed from vyos.configverify import verify_vrf from vyos.utils.network import is_addr_assigned from vyos.utils.process import call from vyos.template import render +from vyos.template import is_ipv4 +from vyos.template import is_ipv6 from vyos import ConfigError from vyos import airbag airbag.enable() @@ -46,9 +47,6 @@ def get_config(config=None): syslog.update({ 'logrotate' : logrotate_conf }) - tmp = is_node_changed(conf, base + ['vrf']) - if tmp: syslog.update({'restart_required': {}}) - syslog = conf.merge_defaults(syslog, recursive=True) if syslog.from_defaults(['local']): del syslog['local'] @@ -74,19 +72,26 @@ def verify(syslog): Warning('No "system domain-name" defined - cannot set syslog FQDN!') if 'remote' in syslog: - for host, host_options in syslog['remote'].items(): - if 'protocol' in host_options and host_options['protocol'] == 'udp': - if 'format' in host_options and 'octet_counted' in host_options['format']: - Warning(f'Syslog UDP transport for "{host}" should not use octet-counted format!') - - verify_vrf(syslog) - - if 'source_address' in syslog: - syslog_vrf = None - if 'vrf' in syslog: - syslog_vrf = syslog['vrf'] - if not is_addr_assigned(syslog['source_address'], syslog_vrf): - raise ConfigError('No interface with given address specified!') + for remote, remote_options in syslog['remote'].items(): + if 'protocol' in remote_options and remote_options['protocol'] == 'udp': + if 'format' in remote_options and 'octet_counted' in remote_options['format']: + Warning(f'Syslog UDP transport for "{remote}" should not use octet-counted format!') + + if 'vrf' in remote_options: + verify_vrf(remote_options) + + if 'source_address' in remote_options: + vrf = None + if 'vrf' in remote_options: + vrf = remote_options['vrf'] + if not is_addr_assigned(remote_options['source_address'], vrf): + raise ConfigError('No interface with given address specified!') + + source_address = remote_options['source_address'] + if ((is_ipv4(remote) and is_ipv6(source_address)) or + (is_ipv6(remote) and is_ipv4(source_address))): + raise ConfigError(f'Source-address "{source_address}" does not match '\ + f'address-family of remote "{remote}"!') def generate(syslog): if not syslog: @@ -108,12 +113,7 @@ def apply(syslog): call(f'systemctl stop {systemd_service} {systemd_socket}') return None - # 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' - - call(f'systemctl {systemd_action} {systemd_service}') + call(f'systemctl reload-or-restart {systemd_service}') return None if __name__ == '__main__': diff --git a/src/migration-scripts/system/28-to-29 b/src/migration-scripts/system/28-to-29 index 2ccd4b9c0..ccf7056c4 100644 --- a/src/migration-scripts/system/28-to-29 +++ b/src/migration-scripts/system/28-to-29 @@ -54,6 +54,11 @@ def migrate(config: ConfigTree) -> None: if config.exists(base + ['global']): config.rename(base + ['global'], 'local') + vrf = '' + if config.exists(base + ['vrf']): + vrf = config.return_value(base + ['vrf']) + config.delete(base + ['vrf']) + # Rename host x.x.x.x -> remote x.x.x.x if config.exists(base + ['host']): config.set(base + ['remote']) @@ -61,4 +66,6 @@ def migrate(config: ConfigTree) -> None: for remote in config.list_nodes(base + ['host']): config.copy(base + ['host', remote], base + ['remote', remote]) config.set_tag(base + ['remote']) + if vrf: + config.set(base + ['remote', remote, 'vrf'], value=vrf) config.delete(base + ['host']) |