Page MenuHomeVyOS Platform

No OneTemporary

Size
69 KB
Referenced Files
None
Subscribers
None
diff --git a/data/templates/snmp/etc.snmp.conf.tmpl b/data/templates/snmp/etc.snmp.conf.tmpl
index 159578906..6e4c6f063 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 %}
clientaddr {{ trap_source }}
{% endif %}
diff --git a/data/templates/snmp/etc.snmpd.conf.tmpl b/data/templates/snmp/etc.snmpd.conf.tmpl
index 1659abf93..278506350 100644
--- a/data/templates/snmp/etc.snmpd.conf.tmpl
+++ b/data/templates/snmp/etc.snmpd.conf.tmpl
@@ -1,118 +1,115 @@
### Autogenerated by snmp.py ###
# non configurable defaults
sysObjectID 1.3.6.1.4.1.44641
sysServices 14
master agentx
agentXPerms 0777 0777
pass .1.3.6.1.2.1.31.1.1.1.18 /opt/vyatta/sbin/if-mib-alias
smuxpeer .1.3.6.1.2.1.83
smuxpeer .1.3.6.1.2.1.157
smuxsocket localhost
# linkUp/Down configure the Event MIB tables to monitor
# the ifTable for network interfaces being taken up or down
# for making internal queries to retrieve any necessary information
iquerySecName {{ vyos_user }}
# Modified from the default linkUpDownNotification
# to include more OIDs and poll more frequently
notificationEvent linkUpTrap linkUp ifIndex ifDescr ifType ifAdminStatus ifOperStatus
notificationEvent linkDownTrap linkDown ifIndex ifDescr ifType ifAdminStatus ifOperStatus
monitor -r 10 -e linkUpTrap "Generate linkUp" ifOperStatus != 2
monitor -r 10 -e linkDownTrap "Generate linkDown" ifOperStatus == 2
########################
# configurable section #
########################
# Default system description is VyOS version
sysDescr VyOS {{ version }}
{% if description %}
# Description
SysDescr {{ description }}
-{%- endif %}
+{% endif %}
# Listen
agentaddress unix:/run/snmpd.socket{% if listen_on %}{% for li in listen_on %},{{ li }}{% endfor %}{% else %},udp:161{% if ipv6_enabled %},udp6:161{% endif %}{% endif %}
# SNMP communities
-{%- for c in communities %}
-
-{%- if c.network_v4 %}
-{%- for network in c.network_v4 %}
+{% 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 %}
+{% endfor %}
+{% elif not c.has_source %}
{{ c.authorization }}community {{ c.name }}
-{%- endif %}
-
-{%- if c.network_v6 %}
-{%- for network in c.network_v6 %}
+{% endif %}
+{% if c.network_v6 %}
+{% for network in c.network_v6 %}
{{ c.authorization }}community6 {{ c.name }} {{ network }}
-{%- endfor %}
-{%- elif not c.has_source %}
+{% endfor %}
+{% elif not c.has_source %}
{{ c.authorization }}community6 {{ c.name }}
-{%- endif %}
-
-{%- endfor %}
+{% endif %}
+{% endfor %}
{% if contact %}
# system contact information
SysContact {{ contact }}
-{%- endif %}
+{% endif %}
{% if location %}
# system location information
SysLocation {{ location }}
-{%- endif %}
+{% endif %}
-{% if smux_peers -%}
+{% if smux_peers %}
# additional smux peers
-{%- for sp in smux_peers %}
+{% for sp in smux_peers %}
smuxpeer {{ sp }}
-{%- endfor %}
-{%- endif %}
+{% endfor %}
+{% endif %}
-{% if trap_targets -%}
+{% if trap_targets %}
# if there is a problem - tell someone!
-{%- for t in trap_targets %}
-trap2sink {{ t.target }}{% if t.port -%}:{{ t.port }}{% endif %} {{ t.community }}
-{%- endfor %}
-{%- endif %}
+{% for trap in trap_targets %}
+trap2sink {{ trap.target }}{{ ":" + trap.port if trap.port is defined }} {{ trap.community }}
+{% endfor %}
+{% endif %}
-{%- if v3_enabled %}
+{% if v3_enabled %}
#
# SNMPv3 stuff goes here
#
# views
-{%- for v in v3_views %}
-{%- for oid in v.oids %}
-view {{ v.name }} included .{{ oid.oid }}
-{%- endfor %}
-{%- endfor %}
+{% for view in v3_views %}
+{% for oid in view.oids %}
+view {{ view.name }} included .{{ oid.oid }}
+{% endfor %}
+{% endfor %}
# access
# context sec.model sec.level match read write notif
-{%- for g in v3_groups %}
-access {{ g.name }} "" usm {{ g.seclevel }} exact {{ g.view }} {% if g.mode == 'ro' %}none{% else %}{{ g.view }}{% endif %} none
-{%- endfor %}
+{% 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 %}
# trap-target
-{%- for t in v3_traps %}
+{% 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 %}
+{% endfor %}
# group
-{%- for u in v3_users %}
+{% for u in v3_users %}
group {{ u.group }} usm {{ u.name }}
-{% endfor %}
-{%- endif %}
+{% endfor %}
+{% endif %}
{% if script_ext %}
# extension scripts
-{%- for ext in script_ext|sort(attribute='name') %}
+{% for ext in script_ext|sort(attribute='name') %}
extend {{ ext.name }} {{ ext.script }}
-{%- endfor %}
+{% endfor %}
{% endif %}
diff --git a/data/templates/snmp/var.snmpd.conf.tmpl b/data/templates/snmp/var.snmpd.conf.tmpl
index 0b8e9f291..6cbc687ef 100644
--- a/data/templates/snmp/var.snmpd.conf.tmpl
+++ b/data/templates/snmp/var.snmpd.conf.tmpl
@@ -1,16 +1,14 @@
### Autogenerated by snmp.py ###
# user
{%- for u in v3_users %}
{%- if u.authOID == 'none' %}
createUser {{ u.name }}
-{%- elif u.authPassword %}
-createUser {{ u.name }} {{ u.authProtocol | upper }} "{{ u.authPassword }}" {{ u.privProtocol | upper }} {{ u.privPassword }}
{%- else %}
-usmUser 1 3 {{ v3_engineid }} "{{ u.name }}" "{{ u.name }}" NULL {{ u.authOID }} {{ u.authMasterKey }} {{ u.privOID }} {{ u.privMasterKey }} 0x
+usmUser 1 3 0x{{ v3_engineid }} "{{ u.name }}" "{{ u.name }}" NULL {{ u.authOID }} 0x{{ u.authMasterKey }} {{ u.privOID }} 0x{{ u.privMasterKey }} 0x
{%- endif %}
{%- endfor %}
createUser {{ vyos_user }} MD5 "{{ vyos_user_pass }}" DES
{%- if v3_engineid %}
-oldEngineID {{ v3_engineid }}
+oldEngineID 0x{{ v3_engineid }}
{%- endif %}
diff --git a/interface-definitions/snmp.xml.in b/interface-definitions/snmp.xml.in
index 4894d0ce8..2fe8ce583 100644
--- a/interface-definitions/snmp.xml.in
+++ b/interface-definitions/snmp.xml.in
@@ -1,631 +1,631 @@
<?xml version="1.0"?>
<!-- SNMP forwarder configuration -->
<interfaceDefinition>
<node name="service">
<children>
<node name="snmp" owner="${vyos_conf_scripts_dir}/snmp.py">
<properties>
<help>Simple Network Management Protocol (SNMP)</help>
<priority>980</priority>
</properties>
<children>
<tagNode name="community">
<properties>
<help>Community name</help>
<constraint>
<regex>^[a-zA-Z0-9\-_]{1,100}$</regex>
</constraint>
<constraintErrorMessage>Community string is limited to alphanumerical characters only with a total lenght of 100</constraintErrorMessage>
</properties>
<children>
<leafNode name="authorization">
<properties>
<help>Authorization type (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>
<leafNode name="client">
<properties>
<help>IP address of SNMP client allowed to contact system</help>
<constraint>
<validator name="ipv4-address"/>
<validator name="ipv6-address"/>
</constraint>
<multi/>
</properties>
</leafNode>
<leafNode name="network">
<properties>
<help>Subnet of SNMP client(s) allowed to contact system</help>
<valueHelp>
<format>ipv4net</format>
<description>IP address and prefix length</description>
</valueHelp>
<valueHelp>
<format>ipv6net</format>
<description>IPv6 address and prefix length</description>
</valueHelp>
<constraint>
<validator name="ip-prefix"/>
</constraint>
<multi/>
</properties>
</leafNode>
</children>
</tagNode>
<leafNode name="contact">
<properties>
<help>Contact information</help>
<constraint>
<regex>^.{1,255}$</regex>
</constraint>
<constraintErrorMessage>Contact information is limited to 255 characters or less</constraintErrorMessage>
</properties>
</leafNode>
<leafNode name="description">
<properties>
<help>Description information</help>
<constraint>
<regex>^.{1,255}$</regex>
</constraint>
<constraintErrorMessage>Description is limited to 255 characters or less</constraintErrorMessage>
</properties>
</leafNode>
<tagNode name="listen-address">
<properties>
<help>IP address to listen for incoming SNMP requests</help>
<valueHelp>
<format>ipv4</format>
<description>IPv4 address to listen for incoming SNMP requests</description>
</valueHelp>
<valueHelp>
<format>ipv6</format>
<description>IPv6 address to listen for incoming SNMP requests</description>
</valueHelp>
<constraint>
<validator name="ipv4-address"/>
<validator name="ipv6-address"/>
</constraint>
</properties>
<children>
<leafNode name="port">
<properties>
<help>Port for SNMP service (default: '161')</help>
<valueHelp>
<format>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>
</leafNode>
</children>
</tagNode>
<leafNode name="location">
<properties>
<help>Location information</help>
<constraint>
<regex>^.{1,255}$</regex>
</constraint>
<constraintErrorMessage>Location is limited to 255 characters or less</constraintErrorMessage>
</properties>
</leafNode>
<leafNode name="smux-peer">
<properties>
<help>Register a subtree for SMUX-based processing</help>
<valueHelp>
<format>oid</format>
<description>Object Identifier</description>
</valueHelp>
<multi/>
</properties>
</leafNode>
<leafNode name="trap-source">
<properties>
<help>SNMP trap source address</help>
<valueHelp>
<format>ipv4</format>
<description>IPv4 address</description>
</valueHelp>
<valueHelp>
<format>ipv6</format>
<description>IPv6 address</description>
</valueHelp>
<constraint>
<validator name="ipv4-address"/>
<validator name="ipv6-address"/>
</constraint>
</properties>
</leafNode>
<tagNode name="trap-target">
<properties>
<help>Address of trap target</help>
<valueHelp>
<format>ipv4</format>
<description>IPv4 address</description>
</valueHelp>
<valueHelp>
<format>ipv6</format>
<description>IPv6 address</description>
</valueHelp>
<constraint>
<validator name="ipv4-address"/>
<validator name="ipv6-address"/>
</constraint>
</properties>
<children>
<leafNode name="community">
<properties>
<help>Community used when sending trap information</help>
</properties>
</leafNode>
<leafNode name="port">
<properties>
<help>Destination port used for trap notification</help>
<valueHelp>
<format>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>
</leafNode>
</children>
</tagNode>
<node name="v3">
<properties>
<help>Simple Network Management Protocol (SNMP) v3</help>
</properties>
<children>
<leafNode name="engineid">
<properties>
- <help>Specifies the EngineID that uniquely identify an agent (e.g. 0xff42)</help>
+ <help>Specifies the EngineID that uniquely identify an agent (e.g. 000000000000000000000002)</help>
<constraint>
- <regex>(0x){0,1}([0-9a-f][0-9a-f]){1,18}$</regex>
+ <regex>^([0-9a-f][0-9a-f]){1,18}$</regex>
</constraint>
<constraintErrorMessage>ID must contain an even number (from 2 to 36) of hex digits</constraintErrorMessage>
</properties>
</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>
<leafNode name="seclevel">
<properties>
<help>Security levels</help>
<completionHelp>
<list>noauth auth priv</list>
</completionHelp>
<valueHelp>
<format>noauth</format>
<description>Messages not authenticated and not encrypted (noAuthNoPriv)</description>
</valueHelp>
<valueHelp>
<format>auth</format>
<description>Messages are authenticated but not encrypted (authNoPriv)</description>
</valueHelp>
<valueHelp>
<format>priv</format>
<description>Messages are authenticated and encrypted (authPriv)</description>
</valueHelp>
<constraint>
<regex>^(noauth|auth|priv)$</regex>
</constraint>
</properties>
</leafNode>
<leafNode name="view">
<properties>
<help>Defines the name of view</help>
<completionHelp>
<path>service snmp v3 view</path>
</completionHelp>
</properties>
</leafNode>
</children>
</tagNode>
<tagNode name="trap-target">
<properties>
<help>Defines SNMP target for inform or traps for IP</help>
<valueHelp>
<format>ipv4</format>
<description>IP address of trap target</description>
</valueHelp>
<valueHelp>
<format>ipv6</format>
<description>IPv6 address of trap target</description>
</valueHelp>
<constraint>
<validator name="ipv4-address"/>
<validator name="ipv6-address"/>
</constraint>
</properties>
<children>
<node name="auth">
<properties>
<help>Defines the privacy</help>
</properties>
<children>
- <leafNode name="encrypted-key">
+ <leafNode name="encrypted-password">
<properties>
<help>Defines the encrypted key for authentication</help>
<constraint>
- <regex>0x[0-9a-f]*$</regex>
+ <regex>^[0-9a-f]*$</regex>
</constraint>
<constraintErrorMessage>Encrypted key must only contain hex digits</constraintErrorMessage>
</properties>
</leafNode>
- <leafNode name="plaintext-key">
+ <leafNode name="plaintext-password">
<properties>
<help>Defines the clear text key for authentication</help>
<constraint>
<regex>^.{8,}$</regex>
</constraint>
<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>
</children>
</node>
<leafNode name="port">
<properties>
<help>Specifies TCP/UDP port of destination SNMP traps/informs (default: '162')</help>
<valueHelp>
<format>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>
</leafNode>
<node name="privacy">
<properties>
<help>Defines the privacy</help>
</properties>
<children>
- <leafNode name="encrypted-key">
+ <leafNode name="encrypted-password">
<properties>
<help>Defines the encrypted key for privacy protocol</help>
<constraint>
- <regex>0x[0-9a-f]*$</regex>
+ <regex>^[0-9a-f]*$</regex>
</constraint>
<constraintErrorMessage>Encrypted key must only contain hex digits</constraintErrorMessage>
</properties>
</leafNode>
- <leafNode name="plaintext-key">
+ <leafNode name="plaintext-password">
<properties>
<help>Defines the clear text key for privacy protocol</help>
<constraint>
<regex>^.{8,}$</regex>
</constraint>
<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>
</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>
<leafNode name="type">
<properties>
<help>Specifies the type of notification between inform and trap (default: 'inform')</help>
<completionHelp>
<list>inform trap</list>
</completionHelp>
<valueHelp>
<format>inform</format>
<description>Use INFORM</description>
</valueHelp>
<valueHelp>
<format>trap</format>
<description>Use TRAP</description>
</valueHelp>
<constraint>
<regex>^(inform|trap)$</regex>
</constraint>
</properties>
</leafNode>
<leafNode name="user">
<properties>
<help>Defines username for authentication</help>
<completionHelp>
<path>service snmp v3 user</path>
</completionHelp>
</properties>
</leafNode>
</children>
</tagNode>
<tagNode name="user">
<properties>
<help>Specifies the user with name username</help>
<constraint>
<regex>[^\(\)\|\-]+$</regex>
</constraint>
<constraintErrorMessage>Illegal characters in name</constraintErrorMessage>
</properties>
<children>
<node name="auth">
<properties>
<help>Specifies the auth</help>
</properties>
<children>
- <leafNode name="encrypted-key">
+ <leafNode name="encrypted-password">
<properties>
<help>Defines the encrypted key for authentication</help>
<constraint>
- <regex>0x[0-9a-f]*$</regex>
+ <regex>^[0-9a-f]*$</regex>
</constraint>
<constraintErrorMessage>Encrypted key must only contain hex digits</constraintErrorMessage>
</properties>
</leafNode>
- <leafNode name="plaintext-key">
+ <leafNode name="plaintext-password">
<properties>
<help>Defines the clear text key for authentication</help>
<constraint>
<regex>^.{8,}$</regex>
</constraint>
<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>
</children>
</node>
<leafNode name="group">
<properties>
<help>Specifies group for user name</help>
<completionHelp>
<path>service snmp v3 group</path>
</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>
<node name="privacy">
<properties>
<help>Defines the privacy</help>
</properties>
<children>
- <leafNode name="encrypted-key">
+ <leafNode name="encrypted-password">
<properties>
<help>Defines the encrypted key for privacy protocol</help>
<constraint>
- <regex>0x[0-9a-f]*$</regex>
+ <regex>^[0-9a-f]*$</regex>
</constraint>
<constraintErrorMessage>Encrypted key must only contain hex digits</constraintErrorMessage>
</properties>
</leafNode>
- <leafNode name="plaintext-key">
+ <leafNode name="plaintext-password">
<properties>
<help>Defines the clear text key for privacy protocol</help>
<constraint>
<regex>^.{8,}$</regex>
</constraint>
<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>
</children>
</node>
</children>
</tagNode>
<tagNode name="view">
<properties>
<help>Specifies the view with name viewname</help>
<constraint>
<regex>[^\(\)\|\-]+$</regex>
</constraint>
<constraintErrorMessage>Illegal characters in name</constraintErrorMessage>
</properties>
<children>
<tagNode name="oid">
<properties>
<help>Specifies the oid</help>
<constraint>
<regex>^[0-9]+(\.[0-9]+)*$</regex>
</constraint>
<constraintErrorMessage>OID must start from a number</constraintErrorMessage>
</properties>
<children>
<leafNode name="exclude">
<properties>
<help>Exclude is an optional argument</help>
</properties>
</leafNode>
<leafNode name="mask">
<properties>
<help>Defines a bit-mask that is indicating which subidentifiers of the associated subtree OID should be regarded as significant</help>
<constraint>
<regex>^[0-9a-f]{2}([\.:][0-9a-f]{2})*$</regex>
</constraint>
<constraintErrorMessage>MASK is a list of hex octets, separated by '.' or ':'</constraintErrorMessage>
</properties>
</leafNode>
</children>
</tagNode>
</children>
</tagNode>
</children>
</node>
<node name="script-extensions">
<properties>
<help>SNMP script extensions</help>
</properties>
<children>
<tagNode name="extension-name">
<properties>
<help>Extension name</help>
<constraint>
<regex>^[a-z0-9\.\-\_]+</regex>
</constraint>
<constraintErrorMessage>Script extension contains invalid characters</constraintErrorMessage>
</properties>
<children>
<leafNode name="script">
<properties>
<help>Script location and name</help>
<completionHelp>
<script>ls /config/user-data</script>
</completionHelp>
<constraint>
<regex>^[a-z0-9\.\-\_\/]+</regex>
</constraint>
<constraintErrorMessage>Script extension contains invalid characters</constraintErrorMessage>
</properties>
</leafNode>
</children>
</tagNode>
</children>
</node>
#include <include/interface-vrf.xml.i>
</children>
</node>
</children>
</node>
</interfaceDefinition>
diff --git a/python/vyos/snmpv3_hashgen.py b/python/vyos/snmpv3_hashgen.py
new file mode 100644
index 000000000..38eca5314
--- /dev/null
+++ b/python/vyos/snmpv3_hashgen.py
@@ -0,0 +1,51 @@
+# Imported from https://github.com/TheMysteriousX/SNMPv3-Hash-Generator
+
+import hashlib
+import string
+import secrets
+
+from itertools import repeat
+
+P_LEN = 32
+E_LEN = 16
+
+class Hashgen(object):
+ @staticmethod
+ def md5(bytes):
+ return hashlib.md5(bytes).digest().hex()
+
+ @staticmethod
+ def sha1(bytes):
+ return hashlib.sha1(bytes).digest().hex()
+
+ @staticmethod
+ def expand(s, l):
+ reps = l // len(s) + 1 # approximation; worst case: overrun = l + len(s)
+ return ''.join(list(repeat(s, reps)))[:l]
+
+ @classmethod
+ def kdf(cls, password):
+ data = cls.expand(password, 1048576).encode('utf-8')
+ return hashlib.sha1(data).digest()
+
+ @staticmethod
+ def random_string(len=P_LEN, alphabet=(string.ascii_letters + string.digits)):
+ return ''.join(secrets.choice(alphabet) for _ in range(len))
+
+ @staticmethod
+ def random_engine(len=E_LEN):
+ return secrets.token_hex(len)
+
+ @classmethod
+ def derive_msg(cls, passphrase, engine):
+ # Parameter derivation á la rfc3414
+ Ku = cls.kdf(passphrase)
+ E = bytearray.fromhex(engine)
+
+ return b''.join([Ku, E, Ku])
+
+ # Define available hash algorithms
+ algs = {
+ 'sha1': sha1,
+ 'md5': md5,
+ }
diff --git a/src/conf_mode/snmp.py b/src/conf_mode/snmp.py
index bafd26edc..f3c91d987 100755
--- a/src/conf_mode/snmp.py
+++ b/src/conf_mode/snmp.py
@@ -1,605 +1,581 @@
#!/usr/bin/env python3
#
# Copyright (C) 2018-2020 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 binascii import hexlify
-from netifaces import interfaces
-from time import sleep
from sys import exit
from vyos.config import Config
from vyos.configverify import verify_vrf
+from vyos.snmpv3_hashgen import Hashgen
+from vyos.template import render
+from vyos.util import call
from vyos.validate import is_ipv4, is_addr_assigned
from vyos.version import get_version_data
-from vyos import ConfigError
-from vyos.util import call
-from vyos.template import render
-
-from vyos import airbag
+from vyos import ConfigError, 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'
default_script_dir = r'/config/user-data/'
systemd_override = r'/etc/systemd/system/snmpd.service.d/override.conf'
# 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' : '',
'description' : '',
'contact' : '',
'trap_source': '',
'trap_targets': [],
'vyos_user': '',
'vyos_user_pass': '',
- 'version': '999',
+ '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
else:
if conf.exists('system ipv6 disable'):
snmp['ipv6_enabled'] = False
conf.set_level('service snmp')
version_data = get_version_data()
snmp['version'] = version_data['version']
# create an internal snmpv3 user of the form 'vyosxxxxxxxxxxxxxxxx'
# os.urandom(8) returns 8 bytes of random data
- snmp['vyos_user'] = 'vyos' + hexlify(os.urandom(8)).decode('utf-8')
- snmp['vyos_user_pass'] = hexlify(os.urandom(16)).decode('utf-8')
+ snmp['vyos_user'] = 'vyos' + Hashgen.random_string(len=8)
+ snmp['vyos_user_pass'] = Hashgen.random_string(len=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))
# 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('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('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-key'.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-key'.format(trap))
+ trap_cfg['authPassword'] = conf.return_value('v3 trap-target {0} auth plaintext-password'.format(trap))
- if conf.exists('v3 trap-target {0} auth encrypted-key'.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-key'.format(trap))
+ 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-key'.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-key'.format(trap))
+ trap_cfg['privPassword'] = conf.return_value('v3 trap-target {0} privacy plaintext-password'.format(trap))
- if conf.exists('v3 trap-target {0} privacy encrypted-key'.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-key'.format(trap))
+ 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-key'.format(user)):
- user_cfg['authMasterKey'] = conf.return_value('v3 user {0} auth encrypted-key'.format(user))
+ 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-key'.format(user)):
- user_cfg['authPassword'] = conf.return_value('v3 user {0} auth plaintext-key'.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-key'.format(user)):
- user_cfg['privMasterKey'] = conf.return_value('v3 user {0} privacy encrypted-key'.format(user))
+ 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-key'.format(user)):
- user_cfg['privPassword'] = conf.return_value('v3 user {0} privacy plaintext-key'.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)
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!')
return None
### 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']))
else:
chmod_755(ext['script'])
for listen in snmp['listen_address']:
addr = listen[0]
port = listen[1]
if is_ipv4(addr):
# example: udp:127.0.0.1:161
listen = 'udp:' + addr + ':' + port
elif snmp['ipv6_enabled']:
# example: udp6:[::1]:161
listen = 'udp6:' + '[' + 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):
snmp['listen_on'].append(listen)
else:
print('WARNING: SNMP listen address {0} not configured!'.format(addr))
verify_vrf(snmp)
# bail out early if SNMP v3 is not configured
if not snmp['v3_enabled']:
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-key/plaintext-key for trap auth')
+ 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-key or plaintext-key for trap auth')
+ 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-key/plaintext-key for trap privacy')
+ raise ConfigError('Must specify only one of encrypted-password/plaintext-key for trap privacy')
if trap['privPassword'] == '' and trap['privMasterKey'] == '':
- raise ConfigError('Must specify encrypted-key or plaintext-key for trap privacy')
+ raise ConfigError('Must specify encrypted-password or plaintext-key for trap privacy')
if not 'type' in trap.keys():
raise ConfigError('v3 trap: "type" must be specified')
if not 'authPassword' and 'authMasterKey' in trap.keys():
raise ConfigError('v3 trap: "auth" must be specified')
if not 'authProtocol' in trap.keys():
raise ConfigError('v3 trap: "protocol" must be specified')
if not 'privPassword' and 'privMasterKey' in trap.keys():
raise ConfigError('v3 trap: "user" must be specified')
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 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 error:
raise ConfigError('You must create group "{0}" first'.format(user['group']))
- # Depending on the configured security level
- # the user has to provide additional info
- if user['authPassword'] and user['authMasterKey']:
- raise ConfigError('Can not mix "encrypted-key" and "plaintext-key" for user auth')
-
+ # 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-key or plaintext-key for user auth')
-
- if user['privPassword'] and user['privMasterKey']:
- raise ConfigError('Can not mix "encrypted-key" and "plaintext-key" for user privacy')
+ raise ConfigError('Must specify encrypted-password or plaintext-key for user auth')
if user['privPassword'] == '' and user['privMasterKey'] == '':
- raise ConfigError('Must specify encrypted-key or plaintext-key for user privacy')
+ 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')
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]
for file in config_files:
rmfile(file)
- # Reload systemd manager configuration
- call('systemctl daemon-reload')
-
if not snmp:
return None
+ if 'v3_users' in snmp.keys():
+ # 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"
+
+ for user in snmp['v3_users']:
+ hash = Hashgen.sha1 if user['authProtocol'] in 'sha1' else Hashgen.md5
+
+ if user['authPassword']:
+ Kul_auth = Hashgen.derive_msg(user['authPassword'], snmp['v3_engineid'])
+ user['authMasterKey'] = hash(Kul_auth)
+ user['authPassword'] = ''
+
+ 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 user['privPassword']:
+ Kul_priv = Hashgen.derive_msg(user['privPassword'], snmp['v3_engineid'])
+ user['privMasterKey'] = hash(Kul_priv)
+ user['privPassword'] = ''
+
+ 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))
+
# Write client config file
render(config_file_client, 'snmp/etc.snmp.conf.tmpl', snmp)
# Write server config file
render(config_file_daemon, 'snmp/etc.snmpd.conf.tmpl', snmp)
# Write access rights config file
render(config_file_access, 'snmp/usr.snmpd.conf.tmpl', snmp)
# Write access rights config file
render(config_file_user, 'snmp/var.snmpd.conf.tmpl', snmp)
# Write daemon configuration file
render(systemd_override, 'snmp/override.conf.tmpl', snmp)
return None
def apply(snmp):
+ # Always reload systemd manager configuration
+ call('systemctl daemon-reload')
+
if not snmp:
return None
- # Reload systemd manager configuration
- call('systemctl daemon-reload')
# start SNMP daemon
- call("systemctl restart snmpd.service")
-
- if 'vrf' not in snmp.keys():
- # service will be restarted multiple times later on
- while (call('systemctl -q is-active snmpd.service') != 0):
- sleep(0.5)
-
- # 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"
-
- # XXX: actually this whole logic makes less sense - why not calculate the
- # password hashed on our own and write them back into the config? I see
- # no valid reason in waiting for a third party process to do so.
- with open(config_file_user, 'r') as f:
- engineID = ''
- for line in f:
- if line.startswith('usmUser'):
- string = line.split(' ')
- cfg = {
- 'user': string[4].replace(r'"', ''),
- 'auth_pw': string[8],
- 'priv_pw': string[10]
- }
- # No need to take care about the VyOS internal user
- if cfg['user'] == snmp['vyos_user']:
- continue
-
- # Now update the running configuration
- #
- # Currently when executing call() the environment does not
- # have the vyos_libexec_dir variable set, see Phabricator T685.
- call('/opt/vyatta/sbin/my_set service snmp v3 user "{0}" auth encrypted-key "{1}" > /dev/null'.format(cfg['user'], cfg['auth_pw']))
- call('/opt/vyatta/sbin/my_set service snmp v3 user "{0}" privacy encrypted-key "{1}" > /dev/null'.format(cfg['user'], cfg['priv_pw']))
- call('/opt/vyatta/sbin/my_delete service snmp v3 user "{0}" auth plaintext-key > /dev/null'.format(cfg['user']))
- call('/opt/vyatta/sbin/my_delete service snmp v3 user "{0}" privacy plaintext-key > /dev/null'.format(cfg['user']))
+ call('systemctl restart snmpd.service')
# Enable AgentX in FRR
call('vtysh -c "configure terminal" -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)
diff --git a/src/migration-scripts/snmp/1-to-2 b/src/migration-scripts/snmp/1-to-2
new file mode 100755
index 000000000..466a624e6
--- /dev/null
+++ b/src/migration-scripts/snmp/1-to-2
@@ -0,0 +1,89 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2020 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/>.
+
+from sys import argv, exit
+from vyos.configtree import ConfigTree
+
+def migrate_keys(config, path):
+ # authentication: rename node 'encrypted-key' -> 'encrypted-password'
+ config_path_auth = path + ['auth', 'encrypted-key']
+ if config.exists(config_path_auth):
+ config.rename(config_path_auth, 'encrypted-password')
+ config_path_auth = path + ['auth', 'encrypted-password']
+
+ # remove leading '0x' from string if present
+ tmp = config.return_value(config_path_auth)
+ if tmp.startswith(prefix):
+ tmp = tmp.replace(prefix, '')
+ config.set(config_path_auth, value=tmp)
+
+ # privacy: rename node 'encrypted-key' -> 'encrypted-password'
+ config_path_priv = path + ['privacy', 'encrypted-key']
+ if config.exists(config_path_priv):
+ config.rename(config_path_priv, 'encrypted-password')
+ config_path_priv = path + ['privacy', 'encrypted-password']
+
+ # remove leading '0x' from string if present
+ tmp = config.return_value(config_path_priv)
+ if tmp.startswith(prefix):
+ tmp = tmp.replace(prefix, '')
+ config.set(config_path_priv, value=tmp)
+
+if __name__ == '__main__':
+ 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()
+
+ config = ConfigTree(config_file)
+ config_base = ['service', 'snmp', 'v3']
+
+ if not config.exists(config_base):
+ # Nothing to do
+ exit(0)
+ else:
+ # We no longer support hashed values prefixed with '0x' to unclutter
+ # CLI and also calculate the hases in advance instead of retrieving
+ # them after service startup - which was always a bad idea
+ prefix = '0x'
+
+ config_engineid = config_base + ['engineid']
+ if config.exists(config_engineid):
+ tmp = config.return_value(config_engineid)
+ if tmp.startswith(prefix):
+ tmp = tmp.replace(prefix, '')
+ config.set(config_engineid, value=tmp)
+
+ config_user = config_base + ['user']
+ if config.exists(config_user):
+ for user in config.list_nodes(config_user):
+ migrate_keys(config, config_user + [user])
+
+ config_trap = config_base + ['trap-target']
+ if config.exists(config_trap):
+ for trap in config.list_nodes(config_trap):
+ migrate_keys(config, config_trap + [trap])
+
+ try:
+ with open(file_name, 'w') as f:
+ f.write(config.to_string())
+ except OSError as e:
+ print("Failed to save the modified config: {}".format(e))
+ exit(1)

File Metadata

Mime Type
text/x-diff
Expires
Fri, Nov 6, 9:17 AM (21 h, 38 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
4653816
Default Alt Text
(69 KB)

Event Timeline