summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/configd-include.json1
-rw-r--r--data/templates/snmp/etc.snmp.conf.tmpl2
-rw-r--r--data/templates/snmp/etc.snmpd.conf.tmpl151
-rw-r--r--data/templates/snmp/override.conf.tmpl2
-rw-r--r--data/templates/snmp/usr.snmpd.conf.tmpl8
-rw-r--r--data/templates/snmp/var.snmpd.conf.tmpl20
-rw-r--r--interface-definitions/include/snmp/access-mode.xml.i23
-rw-r--r--interface-definitions/include/snmp/authentication-type.xml.i22
-rw-r--r--interface-definitions/include/snmp/privacy-type.xml.i22
-rw-r--r--interface-definitions/include/snmp/protocol.xml.i22
-rw-r--r--interface-definitions/snmp.xml.in236
-rw-r--r--python/vyos/template.py14
-rwxr-xr-xsrc/conf_mode/snmp.py639
13 files changed, 436 insertions, 726 deletions
diff --git a/data/configd-include.json b/data/configd-include.json
index ee4cb0d42..9090cb201 100644
--- a/data/configd-include.json
+++ b/data/configd-include.json
@@ -54,6 +54,7 @@
"service_mdns-repeater.py",
"service_pppoe-server.py",
"service_router-advert.py",
+"snmp.py",
"ssh.py",
"system-ip.py",
"system-ipv6.py",
diff --git a/data/templates/snmp/etc.snmp.conf.tmpl b/data/templates/snmp/etc.snmp.conf.tmpl
index 6e4c6f063..f7d9a3c17 100644
--- a/data/templates/snmp/etc.snmp.conf.tmpl
+++ b/data/templates/snmp/etc.snmp.conf.tmpl
@@ -1,4 +1,4 @@
### Autogenerated by snmp.py ###
-{% if trap_source %}
+{% if trap_source is defined and trap_source is not none %}
clientaddr {{ trap_source }}
{% endif %}
diff --git a/data/templates/snmp/etc.snmpd.conf.tmpl b/data/templates/snmp/etc.snmpd.conf.tmpl
index 30806ce8a..befea0122 100644
--- a/data/templates/snmp/etc.snmpd.conf.tmpl
+++ b/data/templates/snmp/etc.snmpd.conf.tmpl
@@ -33,87 +33,152 @@ interface_replace_old yes
# Default system description is VyOS version
sysDescr VyOS {{ version }}
-{% if description %}
+{% if description is defined and description is not none %}
# Description
SysDescr {{ description }}
{% endif %}
# Listen
-agentaddress unix:/run/snmpd.socket{% if listen_on %}{% for li in listen_on %},{{ li }}{% endfor %}{% else %},{{protocol}}:161{% if ipv6_enabled %},{{protocol}}6:161{% endif %}{% endif %}
+{% set options = [] %}
+{% if listen_address is defined and listen_address is not none %}
+{% for address, address_options in listen_address.items() %}
+{% if address | is_ipv6 %}
+{% set protocol = protocol ~ '6' %}
+{% endif %}
+{% set _ = options.append(protocol ~ ':' ~ address | bracketize_ipv6 ~ ':' ~ address_options.port) %}
+{% endfor %}
+{% else %}
+{% set _ = options.append(protocol ~ ':161') %}
+{% if ipv6_disabled is not defined %}
+{% set _ = options.append(protocol ~ '6:161') %}
+{% endif %}
+{% endif %}
+agentaddress unix:/run/snmpd.socket{{ ',' ~ options | join(',') if options is defined and options is not none }}
# SNMP communities
-{% for c in communities %}
-{% if c.network_v4 %}
-{% for network in c.network_v4 %}
-{{ c.authorization }}community {{ c.name }} {{ network }}
-{% endfor %}
-{% elif not c.has_source %}
-{{ c.authorization }}community {{ c.name }}
-{% endif %}
-{% if c.network_v6 %}
-{% for network in c.network_v6 %}
-{{ c.authorization }}community6 {{ c.name }} {{ network }}
-{% endfor %}
-{% elif not c.has_source %}
-{{ c.authorization }}community6 {{ c.name }}
-{% endif %}
-{% endfor %}
+{% if community is defined and community is not none %}
+{% for comm, comm_config in community.items() %}
+{% if comm_config.client is defined and comm_config.client is not none %}
+{% for client in comm_config.client %}
+{% if client | is_ipv4 %}
+{{ comm_config.authorization }}community {{ comm }} {{ client }}
+{% elif client | is_ipv6 %}
+{{ comm_config.authorization }}community6 {{ comm }} {{ client }}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% if comm_config.network is defined and comm_config.network is not none %}
+{% for network in comm_config.network %}
+{% if network | is_ipv4 %}
+{{ comm_config.authorization }}community {{ comm }} {{ network }}
+{% elif client | is_ipv6 %}
+{{ comm_config.authorization }}community6 {{ comm }} {{ network }}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% if comm_config.client is not defined and comm_config.network is not defined %}
+{{ comm_config.authorization }}community {{ comm }}
+{% endif %}
+{% endfor %}
+{% endif %}
-{% if contact %}
+{% if contact is defined and contact is not none %}
# system contact information
SysContact {{ contact }}
{% endif %}
-{% if location %}
+{% if location is defined and location is not none %}
# system location information
SysLocation {{ location }}
{% endif %}
-{% if smux_peers %}
+{% if smux_peer is defined and smux_peer is not none %}
# additional smux peers
-{% for sp in smux_peers %}
-smuxpeer {{ sp }}
+{% for peer in smux_peer %}
+smuxpeer {{ peer }}
{% endfor %}
{% endif %}
-{% if trap_targets %}
+{% if trap_target is defined and trap_target is not none %}
# if there is a problem - tell someone!
-{% for trap in trap_targets %}
-trap2sink {{ trap.target }}{{ ":" + trap.port if trap.port is defined }} {{ trap.community }}
+{% for trap, trap_config in trap_target.items() %}
+trap2sink {{ trap }}:{{ trap_config.port }} {{ trap_config.community }}
{% endfor %}
{% endif %}
-{% if v3_enabled %}
+{% if v3 is defined and v3 is not none %}
#
# SNMPv3 stuff goes here
#
+{% if v3.view is defined and v3.view is not none %}
# views
-{% for view in v3_views %}
-{% for oid in view.oids %}
-view {{ view.name }} included .{{ oid.oid }}
+{% for view, view_config in v3.view.items() %}
+{% if view_config.oid is defined and view_config.oid is not none %}
+{% for oid in view_config.oid %}
+view {{ view }} included .{{ oid }}
+{% endfor %}
+{% endif %}
{% endfor %}
-{% endfor %}
+{% endif %}
# access
+{% if v3.group is defined and v3.group is not none %}
# context sec.model sec.level match read write notif
-{% for group in v3_groups %}
-access {{ group.name }} "" usm {{ group.seclevel }} exact {{ group.view }} {% if group.mode == 'ro' %}none{% else %}{{ group.view }}{% endif %} none
-{% endfor %}
+{% for group, group_config in v3.group.items() %}
+access {{ group }} "" usm {{ group_config.seclevel }} exact {{ group_config.view }} {% if group_config.mode == 'ro' %}none{% else %}{{ group_config.view }}{% endif %} none
+{% endfor %}
+{% endif %}
# trap-target
-{% for t in v3_traps %}
-trapsess -v 3 {{ '-Ci' if t.type == 'inform' }} -e {{ v3_engineid }} -u {{ t.secName }} -l {{ t.secLevel }} -a {{ t.authProtocol }} {% if t.authPassword %}-A {{ t.authPassword }}{% elif t.authMasterKey %}-3m {{ t.authMasterKey }}{% endif %} -x {{ t.privProtocol }} {% if t.privPassword %}-X {{ t.privPassword }}{% elif t.privMasterKey %}-3M {{ t.privMasterKey }}{% endif %} {{ t.ipProto }}:{{ t.ipAddr }}:{{ t.ipPort }}
-{% endfor %}
+{% if v3.trap_target is defined and v3.trap_target is not none %}
+{% for trap, trap_config in v3.trap_target.items() %}
+{% set options = '' %}
+{% if trap_config.type == 'inform' %}
+{% set options = options ~ ' -Ci' %}
+{% endif %}
+{% if v3.engineid is defined and v3.engineid is not none %}
+{% set options = options ~ ' -e "' ~ v3.engineid ~ '"' %}
+{% endif %}
+{% if trap_config.user is defined and trap_config.user is not none %}
+{% set options = options ~ ' -u ' ~ trap_config.user %}
+{% endif %}
+{% if trap_config.auth is defined and trap_config.auth.plaintext_password is defined or trap_config.auth.encrypted_password is defined %}
+{% set options = options ~ ' -a ' ~ trap_config.auth.type %}
+{% if trap_config.auth.plaintext_password is defined and trap_config.auth.plaintext_password is not none %}
+{% set options = options ~ ' -A ' ~ trap_config.auth.plaintext_password %}
+{% elif trap_config.auth.encrypted_password is defined and trap_config.auth.encrypted_password is not none %}
+{% set options = options ~ ' -3m ' ~ trap_config.auth.encrypted_password %}
+{% endif %}
+{% if trap_config.privacy is defined and trap_config.privacy.plaintext_password is defined or trap_config.privacy.encrypted_password is defined %}
+{% set options = options ~ ' -x ' ~ trap_config.privacy.type %}
+{% if trap_config.privacy.plaintext_password is defined and trap_config.privacy.plaintext_password is not none %}
+{% set options = options ~ ' -X ' ~ trap_config.privacy.plaintext_password %}
+{% elif trap_config.privacy.encrypted_password is defined and trap_config.privacy.encrypted_password is not none %}
+{% set options = options ~ ' -3M ' ~ trap_config.privacy.encrypted_password %}
+{% endif %}
+{% set options = options ~ ' -l authPriv' %}
+{% else %}
+{% set options = options ~ ' -l authNoPriv' %}
+{% endif %}
+{% else %}
+{% set options = options ~ ' -l noAuthNoPriv' %}
+{% endif %}
+trapsess -v 3 {{ options }} {{ trap }}:{{ trap_config.protocol }}:{{ trap_config.port }}
+{% endfor %}
+{% endif %}
# group
-{% for u in v3_users %}
-group {{ u.group }} usm {{ u.name }}
-{% endfor %}
+{% if v3.user is defined and v3.user is not none %}
+{% for user, user_config in v3.user.items() %}
+group {{ user_config.group }} usm {{ user }}
+{% endfor %}
+{% endif %}
+{# SNMPv3 end #}
{% endif %}
-{% if script_ext %}
+{% if script_extensions is defined and script_extensions.extension_name is defined and script_extensions.extension_name is not none %}
# extension scripts
-{% for ext in script_ext|sort(attribute='name') %}
-extend {{ ext.name }} {{ ext.script }}
+{% for script, script_config in script_extensions.extension_name.items() | sort(attribute=script) %}
+extend {{ script }} {{ script_config.script }}
{% endfor %}
{% endif %}
diff --git a/data/templates/snmp/override.conf.tmpl b/data/templates/snmp/override.conf.tmpl
index 2ac45a89f..3b00aab83 100644
--- a/data/templates/snmp/override.conf.tmpl
+++ b/data/templates/snmp/override.conf.tmpl
@@ -1,5 +1,5 @@
{% set vrf_command = 'ip vrf exec ' + vrf + ' ' if vrf is defined else '' %}
-{% set oid_route_table = ' ' if route_table is sameas true else '-I -ipCidrRouteTable,inetCidrRouteTable' %}
+{% set oid_route_table = ' ' if oid_enable is defined and oid_enable == 'route-table' else '-I -ipCidrRouteTable,inetCidrRouteTable' %}
[Unit]
StartLimitIntervalSec=0
After=vyos-router.service
diff --git a/data/templates/snmp/usr.snmpd.conf.tmpl b/data/templates/snmp/usr.snmpd.conf.tmpl
index e2c5ec102..1c688a61e 100644
--- a/data/templates/snmp/usr.snmpd.conf.tmpl
+++ b/data/templates/snmp/usr.snmpd.conf.tmpl
@@ -1,6 +1,8 @@
### Autogenerated by snmp.py ###
-{% for u in v3_users %}
-{{ u.mode }}user {{ u.name }}
-{% endfor %}
+{% if v3 is defined and v3.user is defined and v3.user is not none %}
+{% for user, user_config in v3.user.items() %}
+{{ user_config.mode }}user {{ user }}
+{% endfor %}
+{% endif %}
rwuser {{ vyos_user }}
diff --git a/data/templates/snmp/var.snmpd.conf.tmpl b/data/templates/snmp/var.snmpd.conf.tmpl
index c779587df..5871a8234 100644
--- a/data/templates/snmp/var.snmpd.conf.tmpl
+++ b/data/templates/snmp/var.snmpd.conf.tmpl
@@ -1,14 +1,16 @@
### Autogenerated by snmp.py ###
# user
-{% for u in v3_users %}
-{% if u.authOID == 'none' %}
-createUser {{ u.name }}
-{% else %}
-usmUser 1 3 0x{{ v3_engineid }} "{{ u.name }}" "{{ u.name }}" NULL {{ u.authOID }} 0x{{ u.authMasterKey }} {{ u.privOID }} 0x{{ u.privMasterKey }} 0x
-{% endif %}
-{% endfor %}
+{% if v3 is defined and v3 is not none %}
+{% if v3.user is defined and v3.user is not none %}
+{% for user, user_config in v3.user.items() %}
+usmUser 1 3 0x{{ v3.engineid }} "{{ user }}" "{{ user }}" NULL {{ user_config.auth.type | snmp_auth_oid }} 0x{{ user_config.auth.encrypted_password }} {{ user_config.privacy.type | snmp_auth_oid }} 0x{{ user_config.privacy.encrypted_password }} 0x
+{% endfor %}
+{% endif %}
+# VyOS default user
createUser {{ vyos_user }} MD5 "{{ vyos_user_pass }}" DES
-{% if v3_engineid %}
-oldEngineID 0x{{ v3_engineid }}
+
+{% if v3.engineid is defined and v3.engineid is not none %}
+oldEngineID 0x{{ v3.engineid }}
+{% endif %}
{% endif %}
diff --git a/interface-definitions/include/snmp/access-mode.xml.i b/interface-definitions/include/snmp/access-mode.xml.i
new file mode 100644
index 000000000..1fce2364e
--- /dev/null
+++ b/interface-definitions/include/snmp/access-mode.xml.i
@@ -0,0 +1,23 @@
+<!-- include start from snmp/access-mode.xml.i -->
+<leafNode name="mode">
+ <properties>
+ <help>Define access permission</help>
+ <completionHelp>
+ <list>ro rw</list>
+ </completionHelp>
+ <valueHelp>
+ <format>ro</format>
+ <description>Read-Only (default)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>rw</format>
+ <description>read write</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(ro|rw)$</regex>
+ </constraint>
+ <constraintErrorMessage>Authorization type must be either 'rw' or 'ro'</constraintErrorMessage>
+ </properties>
+ <defaultValue>ro</defaultValue>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/snmp/authentication-type.xml.i b/interface-definitions/include/snmp/authentication-type.xml.i
new file mode 100644
index 000000000..2a545864a
--- /dev/null
+++ b/interface-definitions/include/snmp/authentication-type.xml.i
@@ -0,0 +1,22 @@
+<!-- include start from snmp/authentication-type.xml.i -->
+<leafNode name="type">
+ <properties>
+ <help>Define used protocol</help>
+ <completionHelp>
+ <list>md5 sha</list>
+ </completionHelp>
+ <valueHelp>
+ <format>md5</format>
+ <description>Message Digest 5 (default)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>sha</format>
+ <description>Secure Hash Algorithm</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(md5|sha)$</regex>
+ </constraint>
+ </properties>
+ <defaultValue>md5</defaultValue>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/snmp/privacy-type.xml.i b/interface-definitions/include/snmp/privacy-type.xml.i
new file mode 100644
index 000000000..47a1e632e
--- /dev/null
+++ b/interface-definitions/include/snmp/privacy-type.xml.i
@@ -0,0 +1,22 @@
+<!-- include start from snmp/privacy-type.xml.i -->
+<leafNode name="type">
+ <properties>
+ <help>Defines the protocol for privacy</help>
+ <completionHelp>
+ <list>des aes</list>
+ </completionHelp>
+ <valueHelp>
+ <format>des</format>
+ <description>Data Encryption Standard (default)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>aes</format>
+ <description>Advanced Encryption Standard</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(des|aes)$</regex>
+ </constraint>
+ </properties>
+ <defaultValue>des</defaultValue>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/snmp/protocol.xml.i b/interface-definitions/include/snmp/protocol.xml.i
new file mode 100644
index 000000000..335736724
--- /dev/null
+++ b/interface-definitions/include/snmp/protocol.xml.i
@@ -0,0 +1,22 @@
+<!-- include start from snmp/protocol.xml.i -->
+<leafNode name="protocol">
+ <properties>
+ <help>Protocol to be used (TCP/UDP)</help>
+ <completionHelp>
+ <list>udp tcp</list>
+ </completionHelp>
+ <valueHelp>
+ <format>udp</format>
+ <description>Listen protocol UDP (default)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>tcp</format>
+ <description>Listen protocol TCP</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(udp|tcp)$</regex>
+ </constraint>
+ </properties>
+ <defaultValue>udp</defaultValue>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/snmp.xml.in b/interface-definitions/snmp.xml.in
index 949536fe7..67d3aef9a 100644
--- a/interface-definitions/snmp.xml.in
+++ b/interface-definitions/snmp.xml.in
@@ -20,23 +20,24 @@
<children>
<leafNode name="authorization">
<properties>
- <help>Authorization type (default: 'ro')</help>
+ <help>Authorization type</help>
<completionHelp>
<list>ro rw</list>
</completionHelp>
<valueHelp>
<format>ro</format>
- <description>read only</description>
+ <description>Read-Only (default)</description>
</valueHelp>
<valueHelp>
<format>rw</format>
- <description>read write</description>
+ <description>Read-Write</description>
</valueHelp>
<constraint>
<regex>^(ro|rw)$</regex>
</constraint>
<constraintErrorMessage>Authorization type must be either 'rw' or 'ro'</constraintErrorMessage>
</properties>
+ <defaultValue>ro</defaultValue>
</leafNode>
<leafNode name="client">
<properties>
@@ -105,18 +106,9 @@
</constraint>
</properties>
<children>
+ #include <include/port-number.xml.i>
<leafNode name="port">
- <properties>
- <help>Port for SNMP service (default: '161')</help>
- <valueHelp>
- <format>u32:1-65535</format>
- <description>Numeric IP port</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-65535"/>
- </constraint>
- <constraintErrorMessage>Port number must be in range 1 to 65535</constraintErrorMessage>
- </properties>
+ <defaultValue>161</defaultValue>
</leafNode>
</children>
</tagNode>
@@ -131,50 +123,27 @@
</leafNode>
<leafNode name="oid-enable">
<properties>
- <help>Enable specific oids</help>
- <valueHelp>
- <format>txt</format>
- <description>Enable specific oids</description>
- </valueHelp>
- <valueHelp>
- <format>route-table</format>
- <description>Enable route table oids (ipCidrRouteTable inetCidrRouteTable)</description>
- </valueHelp>
+ <help>Enable specific OIDs</help>
<completionHelp>
<list>route-table</list>
</completionHelp>
- <constraint>
- <regex>^(route-table)$</regex>
- </constraint>
- <constraintErrorMessage>Oid must be 'route-table'</constraintErrorMessage>
- </properties>
- </leafNode>
- <leafNode name="protocol">
- <properties>
- <help>Listen protocol for SNMP</help>
- <completionHelp>
- <list>udp tcp</list>
- </completionHelp>
<valueHelp>
- <format>udp</format>
- <description>Listen protocol UDP (default)</description>
- </valueHelp>
- <valueHelp>
- <format>tcp</format>
- <description>Listen protocol TCP</description>
+ <format>route-table</format>
+ <description>Enable routing table OIDs (ipCidrRouteTable inetCidrRouteTable)</description>
</valueHelp>
<constraint>
- <regex>^(udp|tcp)$</regex>
+ <regex>^(route-table)$</regex>
</constraint>
+ <constraintErrorMessage>OID must be 'route-table'</constraintErrorMessage>
</properties>
- <defaultValue>udp</defaultValue>
</leafNode>
+ #include <include/snmp/protocol.xml.i>
<leafNode name="smux-peer">
<properties>
<help>Register a subtree for SMUX-based processing</help>
<valueHelp>
- <format>oid</format>
- <description>Object Identifier</description>
+ <format>txt</format>
+ <description>SNMP Object Identifier</description>
</valueHelp>
<multi/>
</properties>
@@ -218,18 +187,9 @@
<help>Community used when sending trap information</help>
</properties>
</leafNode>
+ #include <include/port-number.xml.i>
<leafNode name="port">
- <properties>
- <help>Destination port used for trap notification</help>
- <valueHelp>
- <format>u32:1-65535</format>
- <description>Numeric IP port</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-65535"/>
- </constraint>
- <constraintErrorMessage>Port number must be in range 1 to 65535</constraintErrorMessage>
- </properties>
+ <defaultValue>162</defaultValue>
</leafNode>
</children>
</tagNode>
@@ -246,32 +206,14 @@
</constraint>
<constraintErrorMessage>ID must contain an even number (from 2 to 36) of hex digits</constraintErrorMessage>
</properties>
+ <defaultValue></defaultValue>
</leafNode>
<tagNode name="group">
<properties>
<help>Specifies the group with name groupname</help>
</properties>
<children>
- <leafNode name="mode">
- <properties>
- <help>Define group access permission (default: 'ro')</help>
- <completionHelp>
- <list>ro rw</list>
- </completionHelp>
- <valueHelp>
- <format>ro</format>
- <description>read only</description>
- </valueHelp>
- <valueHelp>
- <format>rw</format>
- <description>read write</description>
- </valueHelp>
- <constraint>
- <regex>^(ro|rw)$</regex>
- </constraint>
- <constraintErrorMessage>Authorization type must be either 'rw' or 'ro'</constraintErrorMessage>
- </properties>
- </leafNode>
+ #include <include/snmp/access-mode.xml.i>
<leafNode name="seclevel">
<properties>
<help>Security levels</help>
@@ -284,7 +226,7 @@
</valueHelp>
<valueHelp>
<format>auth</format>
- <description>Messages are authenticated but not encrypted (authNoPriv)</description>
+ <description>Messages are authenticated but not encrypted (authNoPriv, default)</description>
</valueHelp>
<valueHelp>
<format>priv</format>
@@ -294,6 +236,7 @@
<regex>^(noauth|auth|priv)$</regex>
</constraint>
</properties>
+ <defaultValue>auth</defaultValue>
</leafNode>
<leafNode name="view">
<properties>
@@ -345,39 +288,12 @@
<constraintErrorMessage>Key must contain 8 or more characters</constraintErrorMessage>
</properties>
</leafNode>
- <leafNode name="type">
- <properties>
- <help>Defines the protocol used for authentication (default: 'md5')</help>
- <completionHelp>
- <list>md5 sha</list>
- </completionHelp>
- <valueHelp>
- <format>md5</format>
- <description>Message Digest 5</description>
- </valueHelp>
- <valueHelp>
- <format>sha</format>
- <description>Secure Hash Algorithm</description>
- </valueHelp>
- <constraint>
- <regex>^(md5|sha)$</regex>
- </constraint>
- </properties>
- </leafNode>
+ #include <include/snmp/authentication-type.xml.i>
</children>
</node>
+ #include <include/port-number.xml.i>
<leafNode name="port">
- <properties>
- <help>Specifies TCP/UDP port of destination SNMP traps/informs (default: '162')</help>
- <valueHelp>
- <format>u32:1-65535</format>
- <description>Numeric IP port</description>
- </valueHelp>
- <constraint>
- <validator name="numeric" argument="--range 1-65535"/>
- </constraint>
- <constraintErrorMessage>Port number must be in range 1 to 65535</constraintErrorMessage>
- </properties>
+ <defaultValue>162</defaultValue>
</leafNode>
<node name="privacy">
<properties>
@@ -402,54 +318,18 @@
<constraintErrorMessage>Key must contain 8 or more characters</constraintErrorMessage>
</properties>
</leafNode>
- <leafNode name="type">
- <properties>
- <help>Defines the protocol for privacy (default: 'des')</help>
- <completionHelp>
- <list>des aes</list>
- </completionHelp>
- <valueHelp>
- <format>des</format>
- <description>Data Encryption Standard</description>
- </valueHelp>
- <valueHelp>
- <format>aes</format>
- <description>Advanced Encryption Standard</description>
- </valueHelp>
- <constraint>
- <regex>^(des|aes)$</regex>
- </constraint>
- </properties>
- </leafNode>
+ #include <include/snmp/privacy-type.xml.i>
</children>
</node>
- <leafNode name="protocol">
- <properties>
- <help>Defines protocol for notification between TCP and UDP</help>
- <completionHelp>
- <list>tcp udp</list>
- </completionHelp>
- <valueHelp>
- <format>tcp</format>
- <description>Use Transmission Control Protocol for notifications</description>
- </valueHelp>
- <valueHelp>
- <format>udp</format>
- <description>Use User Datagram Protocol for notifications</description>
- </valueHelp>
- <constraint>
- <regex>^(tcp|udp)$</regex>
- </constraint>
- </properties>
- </leafNode>
+ #include <include/snmp/protocol.xml.i>
<leafNode name="type">
<properties>
- <help>Specifies the type of notification between inform and trap (default: 'inform')</help>
+ <help>Specifies the type of notification between inform and trap</help>
<completionHelp>
<list>inform trap</list>
</completionHelp>
<valueHelp>
- <format>inform</format>
+ <format>inform (default)</format>
<description>Use INFORM</description>
</valueHelp>
<valueHelp>
@@ -460,6 +340,7 @@
<regex>^(inform|trap)$</regex>
</constraint>
</properties>
+ <defaultValue>inform</defaultValue>
</leafNode>
<leafNode name="user">
<properties>
@@ -503,25 +384,7 @@
<constraintErrorMessage>Key must contain 8 or more characters</constraintErrorMessage>
</properties>
</leafNode>
- <leafNode name="type">
- <properties>
- <help>Defines the protocol used for authentication (default: 'md5')</help>
- <completionHelp>
- <list>md5 sha</list>
- </completionHelp>
- <valueHelp>
- <format>md5</format>
- <description>Message Digest 5</description>
- </valueHelp>
- <valueHelp>
- <format>sha</format>
- <description>Secure Hash Algorithm</description>
- </valueHelp>
- <constraint>
- <regex>^(md5|sha)$</regex>
- </constraint>
- </properties>
- </leafNode>
+ #include <include/snmp/authentication-type.xml.i>
</children>
</node>
<leafNode name="group">
@@ -532,26 +395,7 @@
</completionHelp>
</properties>
</leafNode>
- <leafNode name="mode">
- <properties>
- <help>Define users access permission (default: 'ro')</help>
- <completionHelp>
- <list>ro rw</list>
- </completionHelp>
- <valueHelp>
- <format>ro</format>
- <description>read only</description>
- </valueHelp>
- <valueHelp>
- <format>rw</format>
- <description>read write</description>
- </valueHelp>
- <constraint>
- <regex>^(ro|rw)$</regex>
- </constraint>
- <constraintErrorMessage>Authorization type must be either 'rw' or 'ro'</constraintErrorMessage>
- </properties>
- </leafNode>
+ #include <include/snmp/access-mode.xml.i>
<node name="privacy">
<properties>
<help>Defines the privacy</help>
@@ -575,25 +419,7 @@
<constraintErrorMessage>Key must contain 8 or more characters</constraintErrorMessage>
</properties>
</leafNode>
- <leafNode name="type">
- <properties>
- <help>Defines the protocol for privacy (default: 'des')</help>
- <completionHelp>
- <list>des aes</list>
- </completionHelp>
- <valueHelp>
- <format>des</format>
- <description>Data Encryption Standard</description>
- </valueHelp>
- <valueHelp>
- <format>aes</format>
- <description>Advanced Encryption Standard</description>
- </valueHelp>
- <constraint>
- <regex>^(des|aes)$</regex>
- </constraint>
- </properties>
- </leafNode>
+ #include <include/snmp/privacy-type.xml.i>
</children>
</node>
</children>
diff --git a/python/vyos/template.py b/python/vyos/template.py
index 29c6b3882..f694b53e0 100644
--- a/python/vyos/template.py
+++ b/python/vyos/template.py
@@ -489,3 +489,17 @@ def get_openvpn_ncp_ciphers(ciphers):
else:
out.append(cipher)
return ':'.join(out).upper()
+
+@register_filter('snmp_auth_oid')
+def snmp_auth_oid(type):
+ if type not in ['md5', 'sha', 'aes', 'des', 'none']:
+ raise ValueError()
+
+ OIDs = {
+ 'md5' : '.1.3.6.1.6.3.10.1.1.2',
+ 'sha' : '.1.3.6.1.6.3.10.1.1.3',
+ 'aes' : '.1.3.6.1.6.3.10.1.2.4',
+ 'des' : '.1.3.6.1.6.3.10.1.2.2',
+ 'none': '.1.3.6.1.6.3.10.1.2.1'
+ }
+ return OIDs[type]
diff --git a/src/conf_mode/snmp.py b/src/conf_mode/snmp.py
index 8475e6808..8ce48780b 100755
--- a/src/conf_mode/snmp.py
+++ b/src/conf_mode/snmp.py
@@ -19,16 +19,18 @@ import os
from sys import exit
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.template import is_ipv4
from vyos.util import call
from vyos.util import chmod_755
+from vyos.util import dict_search
from vyos.validate import is_addr_assigned
from vyos.version import get_version_data
+from vyos.xml import defaults
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -37,57 +39,29 @@ 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'
-default_script_dir = r'/config/user-data/'
systemd_override = r'/etc/systemd/system/snmpd.service.d/override.conf'
+systemd_service = 'snmpd.service'
-# SNMP OIDs used to mark auth/priv type
-OIDs = {
- 'md5' : '.1.3.6.1.6.3.10.1.1.2',
- 'sha' : '.1.3.6.1.6.3.10.1.1.3',
- 'aes' : '.1.3.6.1.6.3.10.1.2.4',
- 'des' : '.1.3.6.1.6.3.10.1.2.2',
- 'none': '.1.3.6.1.6.3.10.1.2.1'
-}
-
-default_config_data = {
- 'listen_on': [],
- 'listen_address': [],
- 'ipv6_enabled': 'True',
- 'communities': [],
- 'smux_peers': [],
- 'location' : '',
- 'protocol' : 'udp',
- 'description' : '',
- 'contact' : '',
- 'route_table': 'False',
- 'trap_source': '',
- 'trap_targets': [],
- 'vyos_user': '',
- 'vyos_user_pass': '',
- 'version': '',
- 'v3_enabled': 'False',
- 'v3_engineid': '',
- 'v3_groups': [],
- 'v3_traps': [],
- 'v3_users': [],
- 'v3_views': [],
- 'script_ext': []
-}
-
-def rmfile(file):
- if os.path.isfile(file):
- os.unlink(file)
-
-def get_config():
- snmp = default_config_data
- conf = Config()
- if not conf.exists('service snmp'):
- return None
+def get_config(config=None):
+ if config:
+ conf = config
else:
- if conf.exists('system ipv6 disable'):
- snmp['ipv6_enabled'] = False
+ 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', 'enable']):
+ snmp.update({'lldp_snmp' : ''})
- conf.set_level('service snmp')
+ if conf.exists(['system', 'ipv6', 'disable']):
+ snmp.update({'ipv6_disabled' : ''})
+
+ if 'deleted' in snmp:
+ return snmp
version_data = get_version_data()
snmp['version'] = version_data['version']
@@ -96,470 +70,207 @@ def get_config():
snmp['vyos_user'] = 'vyos' + random(8)
snmp['vyos_user_pass'] = random(16)
- if conf.exists('community'):
- for name in conf.list_nodes('community'):
- community = {
- 'name': name,
- 'authorization': 'ro',
- 'network_v4': [],
- 'network_v6': [],
- 'has_source' : False
- }
-
- if conf.exists('community {0} authorization'.format(name)):
- community['authorization'] = conf.return_value('community {0} authorization'.format(name))
-
- # Subnet of SNMP client(s) allowed to contact system
- if conf.exists('community {0} network'.format(name)):
- for addr in conf.return_values('community {0} network'.format(name)):
- if is_ipv4(addr):
- community['network_v4'].append(addr)
- else:
- community['network_v6'].append(addr)
-
- # IP address of SNMP client allowed to contact system
- if conf.exists('community {0} client'.format(name)):
- for addr in conf.return_values('community {0} client'.format(name)):
- if is_ipv4(addr):
- community['network_v4'].append(addr)
- else:
- community['network_v6'].append(addr)
-
- if (len(community['network_v4']) > 0) or (len(community['network_v6']) > 0):
- community['has_source'] = True
-
- snmp['communities'].append(community)
-
- if conf.exists('contact'):
- snmp['contact'] = conf.return_value('contact')
-
- if conf.exists('description'):
- snmp['description'] = conf.return_value('description')
-
- if conf.exists('listen-address'):
- for addr in conf.list_nodes('listen-address'):
- port = '161'
- if conf.exists('listen-address {0} port'.format(addr)):
- port = conf.return_value('listen-address {0} port'.format(addr))
-
- snmp['listen_address'].append((addr, port))
+ # 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)
+
+ # We can not merge defaults for tagNodes - those need to be blended in
+ # per tagNode instance
+ if 'listen_address' in default_values:
+ del default_values['listen_address']
+ if 'community' in default_values:
+ del default_values['community']
+ if 'trap_target' in default_values:
+ del default_values['trap_target']
+ if 'v3' in default_values:
+ del default_values['v3']
+ snmp = dict_merge(default_values, snmp)
+
+ if 'listen_address' in snmp:
+ default_values = defaults(base + ['listen-address'])
+ for address in snmp['listen_address']:
+ snmp['listen_address'][address] = dict_merge(
+ default_values, snmp['listen_address'][address])
# 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://phabricator.vyos.net/T850
- if not '127.0.0.1' in conf.list_nodes('listen-address'):
- snmp['listen_address'].append(('127.0.0.1', '161'))
-
- if not '::1' in conf.list_nodes('listen-address'):
- snmp['listen_address'].append(('::1', '161'))
-
- if conf.exists('location'):
- snmp['location'] = conf.return_value('location')
-
- if conf.exists('protocol'):
- snmp['protocol'] = conf.return_value('protocol')
-
- if conf.exists('smux-peer'):
- snmp['smux_peers'] = conf.return_values('smux-peer')
-
- if conf.exists('trap-source'):
- snmp['trap_source'] = conf.return_value('trap-source')
-
- if conf.exists('trap-target'):
- for target in conf.list_nodes('trap-target'):
- trap_tgt = {
- 'target': target,
- 'community': '',
- 'port': ''
- }
-
- if conf.exists('trap-target {0} community'.format(target)):
- trap_tgt['community'] = conf.return_value('trap-target {0} community'.format(target))
-
- if conf.exists('trap-target {0} port'.format(target)):
- trap_tgt['port'] = conf.return_value('trap-target {0} port'.format(target))
-
- snmp['trap_targets'].append(trap_tgt)
-
- if conf.exists('script-extensions'):
- for extname in conf.list_nodes('script-extensions extension-name'):
- conf_script = conf.return_value('script-extensions extension-name {} script'.format(extname))
- # if script has not absolute path, use pre configured path
- if "/" not in conf_script:
- conf_script = default_script_dir + conf_script
-
- extension = {
- 'name': extname,
- 'script' : conf_script
- }
-
- snmp['script_ext'].append(extension)
-
- if conf.exists('oid-enable route-table'):
- snmp['route_table'] = True
-
- if conf.exists('vrf'):
- # Append key to dict but don't place it in the default dictionary.
- # This is required to make the override.conf.tmpl work until we
- # migrate to get_config_dict().
- snmp['vrf'] = conf.return_value('vrf')
-
-
- #########################################################################
- # ____ _ _ __ __ ____ _____ #
- # / ___|| \ | | \/ | _ \ __ _|___ / #
- # \___ \| \| | |\/| | |_) | \ \ / / |_ \ #
- # ___) | |\ | | | | __/ \ V / ___) | #
- # |____/|_| \_|_| |_|_| \_/ |____/ #
- # #
- # now take care about the fancy SNMP v3 stuff, or bail out eraly #
- #########################################################################
- if not conf.exists('v3'):
- return snmp
- else:
- snmp['v3_enabled'] = True
-
- # 'set service snmp v3 engineid'
- if conf.exists('v3 engineid'):
- snmp['v3_engineid'] = conf.return_value('v3 engineid')
-
- # 'set service snmp v3 group'
- if conf.exists('v3 group'):
- for group in conf.list_nodes('v3 group'):
- v3_group = {
- 'name': group,
- 'mode': 'ro',
- 'seclevel': 'auth',
- 'view': ''
- }
-
- if conf.exists('v3 group {0} mode'.format(group)):
- v3_group['mode'] = conf.return_value('v3 group {0} mode'.format(group))
-
- if conf.exists('v3 group {0} seclevel'.format(group)):
- v3_group['seclevel'] = conf.return_value('v3 group {0} seclevel'.format(group))
-
- if conf.exists('v3 group {0} view'.format(group)):
- v3_group['view'] = conf.return_value('v3 group {0} view'.format(group))
-
- snmp['v3_groups'].append(v3_group)
-
- # 'set service snmp v3 trap-target'
- if conf.exists('v3 trap-target'):
- for trap in conf.list_nodes('v3 trap-target'):
- trap_cfg = {
- 'ipAddr': trap,
- 'secName': '',
- 'authProtocol': 'md5',
- 'authPassword': '',
- 'authMasterKey': '',
- 'privProtocol': 'des',
- 'privPassword': '',
- 'privMasterKey': '',
- 'ipProto': 'udp',
- 'ipPort': '162',
- 'type': '',
- 'secLevel': 'noAuthNoPriv'
- }
-
- if conf.exists('v3 trap-target {0} user'.format(trap)):
- # Set the securityName used for authenticated SNMPv3 messages.
- trap_cfg['secName'] = conf.return_value('v3 trap-target {0} user'.format(trap))
-
- if conf.exists('v3 trap-target {0} auth type'.format(trap)):
- # Set the authentication protocol (MD5 or SHA) used for authenticated SNMPv3 messages
- # cmdline option '-a'
- trap_cfg['authProtocol'] = conf.return_value('v3 trap-target {0} auth type'.format(trap))
-
- if conf.exists('v3 trap-target {0} auth plaintext-password'.format(trap)):
- # Set the authentication pass phrase used for authenticated SNMPv3 messages.
- # cmdline option '-A'
- trap_cfg['authPassword'] = conf.return_value('v3 trap-target {0} auth plaintext-password'.format(trap))
-
- if conf.exists('v3 trap-target {0} auth encrypted-password'.format(trap)):
- # Sets the keys to be used for SNMPv3 transactions. These options allow you to set the master authentication keys.
- # cmdline option '-3m'
- trap_cfg['authMasterKey'] = conf.return_value('v3 trap-target {0} auth encrypted-password'.format(trap))
-
- if conf.exists('v3 trap-target {0} privacy type'.format(trap)):
- # Set the privacy protocol (DES or AES) used for encrypted SNMPv3 messages.
- # cmdline option '-x'
- trap_cfg['privProtocol'] = conf.return_value('v3 trap-target {0} privacy type'.format(trap))
-
- if conf.exists('v3 trap-target {0} privacy plaintext-password'.format(trap)):
- # Set the privacy pass phrase used for encrypted SNMPv3 messages.
- # cmdline option '-X'
- trap_cfg['privPassword'] = conf.return_value('v3 trap-target {0} privacy plaintext-password'.format(trap))
-
- if conf.exists('v3 trap-target {0} privacy encrypted-password'.format(trap)):
- # Sets the keys to be used for SNMPv3 transactions. These options allow you to set the master encryption keys.
- # cmdline option '-3M'
- trap_cfg['privMasterKey'] = conf.return_value('v3 trap-target {0} privacy encrypted-password'.format(trap))
-
- if conf.exists('v3 trap-target {0} protocol'.format(trap)):
- trap_cfg['ipProto'] = conf.return_value('v3 trap-target {0} protocol'.format(trap))
-
- if conf.exists('v3 trap-target {0} port'.format(trap)):
- trap_cfg['ipPort'] = conf.return_value('v3 trap-target {0} port'.format(trap))
-
- if conf.exists('v3 trap-target {0} type'.format(trap)):
- trap_cfg['type'] = conf.return_value('v3 trap-target {0} type'.format(trap))
-
- # Determine securityLevel used for SNMPv3 messages (noAuthNoPriv|authNoPriv|authPriv).
- # Appropriate pass phrase(s) must provided when using any level higher than noAuthNoPriv.
- if trap_cfg['authPassword'] or trap_cfg['authMasterKey']:
- if trap_cfg['privProtocol'] or trap_cfg['privPassword']:
- trap_cfg['secLevel'] = 'authPriv'
- else:
- trap_cfg['secLevel'] = 'authNoPriv'
-
- snmp['v3_traps'].append(trap_cfg)
-
- # 'set service snmp v3 user'
- if conf.exists('v3 user'):
- for user in conf.list_nodes('v3 user'):
- user_cfg = {
- 'name': user,
- 'authMasterKey': '',
- 'authPassword': '',
- 'authProtocol': 'md5',
- 'authOID': 'none',
- 'group': '',
- 'mode': 'ro',
- 'privMasterKey': '',
- 'privPassword': '',
- 'privOID': '',
- 'privProtocol': 'des'
- }
-
- # v3 user {0} auth
- if conf.exists('v3 user {0} auth encrypted-password'.format(user)):
- user_cfg['authMasterKey'] = conf.return_value('v3 user {0} auth encrypted-password'.format(user))
-
- if conf.exists('v3 user {0} auth plaintext-password'.format(user)):
- user_cfg['authPassword'] = conf.return_value('v3 user {0} auth plaintext-password'.format(user))
-
- # load default value
- type = user_cfg['authProtocol']
- if conf.exists('v3 user {0} auth type'.format(user)):
- type = conf.return_value('v3 user {0} auth type'.format(user))
-
- # (re-)update with either default value or value from CLI
- user_cfg['authProtocol'] = type
- user_cfg['authOID'] = OIDs[type]
-
- # v3 user {0} group
- if conf.exists('v3 user {0} group'.format(user)):
- user_cfg['group'] = conf.return_value('v3 user {0} group'.format(user))
-
- # v3 user {0} mode
- if conf.exists('v3 user {0} mode'.format(user)):
- user_cfg['mode'] = conf.return_value('v3 user {0} mode'.format(user))
-
- # v3 user {0} privacy
- if conf.exists('v3 user {0} privacy encrypted-password'.format(user)):
- user_cfg['privMasterKey'] = conf.return_value('v3 user {0} privacy encrypted-password'.format(user))
-
- if conf.exists('v3 user {0} privacy plaintext-password'.format(user)):
- user_cfg['privPassword'] = conf.return_value('v3 user {0} privacy plaintext-password'.format(user))
-
- # load default value
- type = user_cfg['privProtocol']
- if conf.exists('v3 user {0} privacy type'.format(user)):
- type = conf.return_value('v3 user {0} privacy type'.format(user))
-
- # (re-)update with either default value or value from CLI
- user_cfg['privProtocol'] = type
- user_cfg['privOID'] = OIDs[type]
-
- snmp['v3_users'].append(user_cfg)
-
- # 'set service snmp v3 view'
- if conf.exists('v3 view'):
- for view in conf.list_nodes('v3 view'):
- view_cfg = {
- 'name': view,
- 'oids': []
- }
-
- if conf.exists('v3 view {0} oid'.format(view)):
- for oid in conf.list_nodes('v3 view {0} oid'.format(view)):
- oid_cfg = {
- 'oid': oid
- }
- view_cfg['oids'].append(oid_cfg)
- snmp['v3_views'].append(view_cfg)
+ 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']:
+ if 'ipv6_disabled' not in snmp:
+ tmp = {'::1': {'port': '161'}}
+ snmp['listen_address'] = dict_merge(tmp, snmp['listen_address'])
+
+ if 'community' in snmp:
+ default_values = defaults(base + ['community'])
+ for community in snmp['community']:
+ snmp['community'][community] = dict_merge(
+ default_values, snmp['community'][community])
+
+ if 'trap_target' in snmp:
+ default_values = defaults(base + ['trap-target'])
+ for trap in snmp['trap_target']:
+ snmp['trap_target'][trap] = dict_merge(
+ default_values, snmp['trap_target'][trap])
+
+ if 'v3' in snmp:
+ default_values = defaults(base + ['v3'])
+ # tagNodes need to be merged in individually later on
+ for tmp in ['user', 'group', 'trap_target']:
+ del default_values[tmp]
+ snmp['v3'] = dict_merge(default_values, snmp['v3'])
+
+ for user_group in ['user', 'group']:
+ if user_group in snmp['v3']:
+ default_values = defaults(base + ['v3', user_group])
+ for tmp in snmp['v3'][user_group]:
+ snmp['v3'][user_group][tmp] = dict_merge(
+ default_values, snmp['v3'][user_group][tmp])
+
+ if 'trap_target' in snmp['v3']:
+ default_values = defaults(base + ['v3', 'trap-target'])
+ for trap in snmp['v3']['trap_target']:
+ snmp['v3']['trap_target'][trap] = dict_merge(
+ default_values, snmp['v3']['trap_target'][trap])
return snmp
def verify(snmp):
- if snmp is None:
- # we can not delete SNMP when LLDP is configured with SNMP
- conf = Config()
- if conf.exists('service lldp snmp enable'):
- raise ConfigError('Can not delete SNMP service, as LLDP still uses SNMP!')
-
+ if not 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 snmp['script_ext']:
- for ext in snmp['script_ext']:
- if not os.path.isfile(ext['script']):
- print ("WARNING: script: {} doesn't exist".format(ext['script']))
+ 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):
+ print(f'WARNING: script "{tmp}" does not exist!')
else:
- chmod_755(ext['script'])
-
- for listen in snmp['listen_address']:
- addr = listen[0]
- port = listen[1]
- protocol = snmp['protocol']
-
- tmp = None
- if is_ipv4(addr):
- # example: udp:127.0.0.1:161
- tmp = f'{protocol}:{addr}:{port}'
- elif snmp['ipv6_enabled']:
- # example: udp6:[::1]:161
- tmp = f'{protocol}6:[{addr}]:{port}'
-
- # We only wan't to configure addresses that exist on the system.
- # Hint the user if they don't exist
- if is_addr_assigned(addr):
- if tmp: snmp['listen_on'].append(tmp)
- else:
- print(f'WARNING: SNMP listen address {addr} not configured!')
+ 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 not is_addr_assigned(address):
+ print(f'WARNING: SNMP listen address "{address}" not configured!')
+
+ 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!')
verify_vrf(snmp)
# bail out early if SNMP v3 is not configured
- if not snmp['v3_enabled']:
+ if 'v3' not in snmp:
return None
- if 'v3_groups' in snmp.keys():
- for group in snmp['v3_groups']:
- #
- # A view must exist prior to mapping it into a group
- #
- if 'view' in group.keys():
- error = True
- if 'v3_views' in snmp.keys():
- for view in snmp['v3_views']:
- if view['name'] == group['view']:
- error = False
- if error:
- raise ConfigError('You must create view "{0}" first'.format(group['view']))
- else:
- raise ConfigError('"view" must be specified')
-
- if not 'mode' in group.keys():
- raise ConfigError('"mode" must be specified')
-
- if not 'seclevel' in group.keys():
- raise ConfigError('"seclevel" must be specified')
-
- if 'v3_traps' in snmp.keys():
- for trap in snmp['v3_traps']:
- if trap['authPassword'] and trap['authMasterKey']:
- raise ConfigError('Must specify only one of encrypted-password/plaintext-key for trap auth')
-
- if trap['authPassword'] == '' and trap['authMasterKey'] == '':
- raise ConfigError('Must specify encrypted-password or plaintext-key for trap auth')
-
- if trap['privPassword'] and trap['privMasterKey']:
- raise ConfigError('Must specify only one of encrypted-password/plaintext-key for trap privacy')
+ 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 trap['privPassword'] == '' and trap['privMasterKey'] == '':
- raise ConfigError('Must specify encrypted-password or plaintext-key for trap privacy')
+ 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 not 'type' in trap.keys():
- raise ConfigError('v3 trap: "type" must be specified')
+ 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 not 'authPassword' and 'authMasterKey' in trap.keys():
- raise ConfigError('v3 trap: "auth" must be specified')
+ 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}"!')
- if not 'authProtocol' in trap.keys():
- raise ConfigError('v3 trap: "protocol" must be specified')
+ # 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 not 'privPassword' and 'privMasterKey' in trap.keys():
- raise ConfigError('v3 trap: "user" must be specified')
+ 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 'v3_users' in snmp.keys():
- for user in snmp['v3_users']:
- #
- # Group must exist prior to mapping it into a group
- # seclevel will be extracted from group
- #
- if 'group' not in user or user['group'] == '':
- username = user['name']
- raise ConfigError(f'Group membership required for user "{username}"!')
+ 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 user['group']:
- error = True
- if 'v3_groups' in snmp.keys():
- for group in snmp['v3_groups']:
- if group['name'] == user['group']:
- seclevel = group['seclevel']
- error = False
+ 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 error:
- raise ConfigError('You must create group "{0}" first'.format(user['group']))
+ 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}"!')
- # Depending on the configured security level the user has to provide additional info
- if (not user['authPassword'] and not user['authMasterKey']):
- raise ConfigError('Must specify encrypted-password or plaintext-key for user auth')
+ 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 user['privPassword'] == '' and user['privMasterKey'] == '':
- raise ConfigError('Must specify encrypted-password or plaintext-key for user privacy')
-
- if user['mode'] == '':
- raise ConfigError('Must specify user mode ro/rw')
-
- if 'v3_views' in snmp.keys():
- for view in snmp['v3_views']:
- if not view['oids']:
- raise ConfigError('Must configure an oid')
+ 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('systemctl stop snmpd.service')
- config_files = [config_file_client, config_file_daemon, config_file_access,
- config_file_user, systemd_override]
+ 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:
- rmfile(file)
+ if os.path.isfile(file):
+ os.unlink(file)
if not snmp:
return None
- if 'v3_users' in snmp.keys():
+ 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"
+ os.environ['vyos_libexec_dir'] = '/usr/libexec/vyos'
- for user in snmp['v3_users']:
- if user['authProtocol'] == 'sha':
- hash = plaintext_to_sha1
- else:
- hash = plaintext_to_md5
+ 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']
- if user['authPassword']:
- user['authMasterKey'] = hash(user['authPassword'], snmp['v3_engineid'])
- user['authPassword'] = ''
+ 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')
- call('/opt/vyatta/sbin/my_set service snmp v3 user "{name}" auth encrypted-password "{authMasterKey}" > /dev/null'.format(**user))
- call('/opt/vyatta/sbin/my_delete service snmp v3 user "{name}" auth plaintext-password > /dev/null'.format(**user))
+ 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))
- if user['privPassword']:
- user['privMasterKey'] = hash(user['privPassword'], snmp['v3_engineid'])
- user['privPassword'] = ''
+ snmp['v3']['user'][user]['privacy']['encrypted_password'] = tmp
+ del snmp['v3']['user'][user]['privacy']['plaintext_password']
- call('/opt/vyatta/sbin/my_set service snmp v3 user "{name}" privacy encrypted-password "{privMasterKey}" > /dev/null'.format(**user))
- call('/opt/vyatta/sbin/my_delete service snmp v3 user "{name}" privacy plaintext-password > /dev/null'.format(**user))
+ 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.tmpl', snmp)
@@ -582,7 +293,7 @@ def apply(snmp):
return None
# start SNMP daemon
- call('systemctl restart snmpd.service')
+ call(f'systemctl restart {systemd_service}')
# Enable AgentX in FRR
call('vtysh -c "configure terminal" -c "agentx" >/dev/null')