diff options
author | Christian Breunig <christian@breunig.cc> | 2023-12-30 23:25:20 +0100 |
---|---|---|
committer | Christian Breunig <christian@breunig.cc> | 2024-01-01 09:25:32 +0100 |
commit | c9eaafd9f808aba8d29be73054e11d37577e539a (patch) | |
tree | aeccfda0a305cf6aca41630900e75bd32961a911 /src/conf_mode/snmp.py | |
parent | 2078253176046ea4d07e69caeb7932ea439b5614 (diff) | |
download | vyos-1x-c9eaafd9f808aba8d29be73054e11d37577e539a.tar.gz vyos-1x-c9eaafd9f808aba8d29be73054e11d37577e539a.zip |
T5474: establish common file name pattern for XML conf mode commands
We will use _ as CLI level divider. The XML definition filename and also
the Python helper should match the CLI node.
Example:
set interfaces ethernet -> interfaces_ethernet.xml.in
set interfaces bond -> interfaces_bond.xml.in
set service dhcp-server -> service_dhcp-server-xml.in
(cherry picked from commit 4ef110fd2c501b718344c72d495ad7e16d2bd465)
Diffstat (limited to 'src/conf_mode/snmp.py')
-rwxr-xr-x | src/conf_mode/snmp.py | 270 |
1 files changed, 0 insertions, 270 deletions
diff --git a/src/conf_mode/snmp.py b/src/conf_mode/snmp.py deleted file mode 100755 index f65d0d6bc..000000000 --- a/src/conf_mode/snmp.py +++ /dev/null @@ -1,270 +0,0 @@ -#!/usr/bin/env python3 -# -# 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 -# 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/>. - -import os - -from sys import exit - -from vyos.base import Warning -from vyos.config import Config -from vyos.configdict import dict_merge -from vyos.configverify import verify_vrf -from vyos.snmpv3_hashgen import plaintext_to_md5 -from vyos.snmpv3_hashgen import plaintext_to_sha1 -from vyos.snmpv3_hashgen import random -from vyos.template import render -from vyos.utils.process import call -from vyos.utils.permission import chmod_755 -from vyos.utils.dict import dict_search -from vyos.utils.network import is_addr_assigned -from vyos.version import get_version_data -from vyos import ConfigError -from vyos import airbag -airbag.enable() - -config_file_client = r'/etc/snmp/snmp.conf' -config_file_daemon = r'/etc/snmp/snmpd.conf' -config_file_access = r'/usr/share/snmp/snmpd.conf' -config_file_user = r'/var/lib/snmp/snmpd.conf' -systemd_override = r'/run/systemd/system/snmpd.service.d/override.conf' -systemd_service = 'snmpd.service' - -def get_config(config=None): - if config: - conf = config - else: - conf = Config() - base = ['service', 'snmp'] - - snmp = conf.get_config_dict(base, key_mangling=('-', '_'), - get_first_key=True, no_tag_node_value_mangle=True) - if not conf.exists(base): - snmp.update({'deleted' : ''}) - - if conf.exists(['service', 'lldp', 'snmp']): - snmp.update({'lldp_snmp' : ''}) - - if 'deleted' in snmp: - return snmp - - version_data = get_version_data() - snmp['version'] = version_data['version'] - - # create an internal snmpv3 user of the form 'vyosxxxxxxxxxxxxxxxx' - snmp['vyos_user'] = 'vyos' + random(8) - snmp['vyos_user_pass'] = random(16) - - # We have gathered the dict representation of the CLI, but there are default - # options which we need to update into the dictionary retrived. - snmp = conf.merge_defaults(snmp, recursive=True) - - if 'listen_address' in snmp: - # Always listen on localhost if an explicit address has been configured - # This is a safety measure to not end up with invalid listen addresses - # that are not configured on this system. See https://vyos.dev/T850 - if '127.0.0.1' not in snmp['listen_address']: - tmp = {'127.0.0.1': {'port': '161'}} - snmp['listen_address'] = dict_merge(tmp, snmp['listen_address']) - - if '::1' not in snmp['listen_address']: - tmp = {'::1': {'port': '161'}} - snmp['listen_address'] = dict_merge(tmp, snmp['listen_address']) - - return snmp - -def verify(snmp): - if 'deleted' in snmp: - return None - - if {'deleted', 'lldp_snmp'} <= set(snmp): - raise ConfigError('Can not delete SNMP service, as LLDP still uses SNMP!') - - ### check if the configured script actually exist - if 'script_extensions' in snmp and 'extension_name' in snmp['script_extensions']: - for extension, extension_opt in snmp['script_extensions']['extension_name'].items(): - if 'script' not in extension_opt: - raise ConfigError(f'Script extension "{extension}" requires an actual script to be configured!') - - tmp = extension_opt['script'] - if not os.path.isfile(tmp): - Warning(f'script "{tmp}" does not exist!') - else: - chmod_755(extension_opt['script']) - - if 'listen_address' in snmp: - for address in snmp['listen_address']: - # We only wan't to configure addresses that exist on the system. - # Hint the user if they don't exist - if 'vrf' in snmp: - vrf_name = snmp['vrf'] - if not is_addr_assigned(address, vrf_name) and address not in ['::1','127.0.0.1']: - raise ConfigError(f'SNMP listen address "{address}" not configured in vrf "{vrf_name}"!') - elif not is_addr_assigned(address): - raise ConfigError(f'SNMP listen address "{address}" not configured in default vrf!') - - if 'trap_target' in snmp: - for trap, trap_config in snmp['trap_target'].items(): - if 'community' not in trap_config: - raise ConfigError(f'Trap target "{trap}" requires a community to be set!') - - if 'oid_enable' in snmp: - Warning(f'Custom OIDs are enabled and may lead to system instability and high resource consumption') - - - verify_vrf(snmp) - - # bail out early if SNMP v3 is not configured - if 'v3' not in snmp: - return None - - if 'user' in snmp['v3']: - for user, user_config in snmp['v3']['user'].items(): - if 'group' not in user_config: - raise ConfigError(f'Group membership required for user "{user}"!') - - if 'plaintext_password' not in user_config['auth'] and 'encrypted_password' not in user_config['auth']: - raise ConfigError(f'Must specify authentication encrypted-password or plaintext-password for user "{user}"!') - - if 'plaintext_password' not in user_config['privacy'] and 'encrypted_password' not in user_config['privacy']: - raise ConfigError(f'Must specify privacy encrypted-password or plaintext-password for user "{user}"!') - - if 'group' in snmp['v3']: - for group, group_config in snmp['v3']['group'].items(): - if 'seclevel' not in group_config: - raise ConfigError(f'Must configure "seclevel" for group "{group}"!') - if 'view' not in group_config: - raise ConfigError(f'Must configure "view" for group "{group}"!') - - # Check if 'view' exists - view = group_config['view'] - if 'view' not in snmp['v3'] or view not in snmp['v3']['view']: - raise ConfigError(f'You must create view "{view}" first!') - - if 'view' in snmp['v3']: - for view, view_config in snmp['v3']['view'].items(): - if 'oid' not in view_config: - raise ConfigError(f'Must configure an "oid" for view "{view}"!') - - if 'trap_target' in snmp['v3']: - for trap, trap_config in snmp['v3']['trap_target'].items(): - if 'plaintext_password' not in trap_config['auth'] and 'encrypted_password' not in trap_config['auth']: - raise ConfigError(f'Must specify one of authentication encrypted-password or plaintext-password for trap "{trap}"!') - - if {'plaintext_password', 'encrypted_password'} <= set(trap_config['auth']): - raise ConfigError(f'Can not specify both authentication encrypted-password and plaintext-password for trap "{trap}"!') - - if 'plaintext_password' not in trap_config['privacy'] and 'encrypted_password' not in trap_config['privacy']: - raise ConfigError(f'Must specify one of privacy encrypted-password or plaintext-password for trap "{trap}"!') - - if {'plaintext_password', 'encrypted_password'} <= set(trap_config['privacy']): - raise ConfigError(f'Can not specify both privacy encrypted-password and plaintext-password for trap "{trap}"!') - - if 'type' not in trap_config: - raise ConfigError('SNMP v3 trap "type" must be specified!') - - return None - -def generate(snmp): - # As we are manipulating the snmpd user database we have to stop it first! - # This is even save if service is going to be removed - call(f'systemctl stop {systemd_service}') - # Clean config files - config_files = [config_file_client, config_file_daemon, - config_file_access, config_file_user, systemd_override] - for file in config_files: - if os.path.isfile(file): - os.unlink(file) - - if 'deleted' in snmp: - return None - - if 'v3' in snmp: - # net-snmp is now regenerating the configuration file in the background - # thus we need to re-open and re-read the file as the content changed. - # After that we can no read the encrypted password from the config and - # replace the CLI plaintext password with its encrypted version. - os.environ['vyos_libexec_dir'] = '/usr/libexec/vyos' - - if 'user' in snmp['v3']: - for user, user_config in snmp['v3']['user'].items(): - if dict_search('auth.type', user_config) == 'sha': - hash = plaintext_to_sha1 - else: - hash = plaintext_to_md5 - - if dict_search('auth.plaintext_password', user_config) is not None: - tmp = hash(dict_search('auth.plaintext_password', user_config), - dict_search('v3.engineid', snmp)) - - snmp['v3']['user'][user]['auth']['encrypted_password'] = tmp - del snmp['v3']['user'][user]['auth']['plaintext_password'] - - call(f'/opt/vyatta/sbin/my_set service snmp v3 user "{user}" auth encrypted-password "{tmp}" > /dev/null') - call(f'/opt/vyatta/sbin/my_delete service snmp v3 user "{user}" auth plaintext-password > /dev/null') - - if dict_search('privacy.plaintext_password', user_config) is not None: - tmp = hash(dict_search('privacy.plaintext_password', user_config), - dict_search('v3.engineid', snmp)) - - snmp['v3']['user'][user]['privacy']['encrypted_password'] = tmp - del snmp['v3']['user'][user]['privacy']['plaintext_password'] - - call(f'/opt/vyatta/sbin/my_set service snmp v3 user "{user}" privacy encrypted-password "{tmp}" > /dev/null') - call(f'/opt/vyatta/sbin/my_delete service snmp v3 user "{user}" privacy plaintext-password > /dev/null') - - # Write client config file - render(config_file_client, 'snmp/etc.snmp.conf.j2', snmp) - # Write server config file - render(config_file_daemon, 'snmp/etc.snmpd.conf.j2', snmp) - # Write access rights config file - render(config_file_access, 'snmp/usr.snmpd.conf.j2', snmp) - # Write access rights config file - render(config_file_user, 'snmp/var.snmpd.conf.j2', snmp) - # Write daemon configuration file - render(systemd_override, 'snmp/override.conf.j2', snmp) - - return None - -def apply(snmp): - # Always reload systemd manager configuration - call('systemctl daemon-reload') - - if 'deleted' in snmp: - return None - - # start SNMP daemon - call(f'systemctl restart {systemd_service}') - - # Enable AgentX in FRR - # This should be done for each daemon individually because common command - # works only if all the daemons started with SNMP support - frr_daemons_list = [ - 'bgpd', 'ospf6d', 'ospfd', 'ripd', 'ripngd', 'isisd', 'ldpd', 'zebra' - ] - for frr_daemon in frr_daemons_list: - call(f'vtysh -c "configure terminal" -d {frr_daemon} -c "agentx" >/dev/null') - - return None - -if __name__ == '__main__': - try: - c = get_config() - verify(c) - generate(c) - apply(c) - except ConfigError as e: - print(e) - exit(1) |