diff options
201 files changed, 3726 insertions, 2479 deletions
| @@ -60,9 +60,10 @@ op_mode_definitions: $(op_xml_obj)  	rm -f $(OP_TMPL_DIR)/show/node.def  	rm -f $(OP_TMPL_DIR)/show/system/node.def -	# XXX: ping must be able to recursivly call itself as the +	# XXX: ping and traceroute must be able to recursivly call itself as the  	# options are provided from the script itself  	ln -s ../node.tag $(OP_TMPL_DIR)/ping/node.tag/node.tag/ +	ln -s ../node.tag $(OP_TMPL_DIR)/traceroute/node.tag/node.tag/  	# XXX: test if there are empty node.def files - this is not allowed as these  	# could mask help strings or mandatory priority statements diff --git a/data/configd-include.json b/data/configd-include.json index da2233bc1..5a4912e30 100644 --- a/data/configd-include.json +++ b/data/configd-include.json @@ -1,7 +1,7 @@  [  "arp.py",  "bcast_relay.py", -"containers.py", +"container.py",  "conntrack.py",  "conntrack_sync.py",  "dhcp_relay.py", diff --git a/data/templates/accel-ppp/chap-secrets.config_dict.tmpl b/data/templates/accel-ppp/chap-secrets.config_dict.j2 index d4e8bb2aa..51e66d57c 100644 --- a/data/templates/accel-ppp/chap-secrets.config_dict.tmpl +++ b/data/templates/accel-ppp/chap-secrets.config_dict.j2 @@ -1,10 +1,10 @@  # username  server  password  acceptable local IP addresses   shaper  {% if authentication.local_users.username is vyos_defined %} -{%   for user, user_config in authentication.local_users.username.items() if user_config.disabled is not vyos_defined %} -{%     if user_config.rate_limit is vyos_defined %} +{%     for user, user_config in authentication.local_users.username.items() if user_config.disabled is not vyos_defined %} +{%         if user_config.rate_limit is vyos_defined %}  {{ "%-12s" | format(user) }} * {{ "%-16s" | format(user_config.password) }} {{ "%-16s" | format(user_config.static_ip) }} {{ user_config.rate_limit.download }}/{{ user_config.rate_limit.upload }} -{%     else %} +{%         else %}  {{ "%-12s" | format(user) }} * {{ "%-16s" | format(user_config.password) }} {{ "%-16s" | format(user_config.static_ip) }} -{%     endif %} -{%   endfor %} +{%         endif %} +{%     endfor %}  {% endif %} diff --git a/data/templates/accel-ppp/chap-secrets.ipoe.tmpl b/data/templates/accel-ppp/chap-secrets.ipoe.j2 index 1df878fcf..a1430ec22 100644 --- a/data/templates/accel-ppp/chap-secrets.ipoe.tmpl +++ b/data/templates/accel-ppp/chap-secrets.ipoe.j2 @@ -1,18 +1,18 @@  # username  server  password  acceptable local IP addresses   shaper  {% for interface in auth_interfaces %} -{% for mac in interface.mac %} -{% if mac.rate_upload and mac.rate_download %} -{% if mac.vlan_id %} +{%     for mac in interface.mac %} +{%         if mac.rate_upload and mac.rate_download %} +{%             if mac.vlan_id %}  {{ interface.name }}.{{ mac.vlan_id }} * {{ mac.address | lower }} * {{ mac.rate_download }}/{{ mac.rate_upload }} -{% else %} +{%             else %}  {{ interface.name }} * {{ mac.address | lower }}  * {{ mac.rate_download }}/{{ mac.rate_upload }} -{% endif %} -{% else %} -{% if mac.vlan_id %} +{%             endif %} +{%         else %} +{%             if mac.vlan_id %}  {{ interface.name }}.{{ mac.vlan_id }} * {{ mac.address | lower }} * -{% else %} +{%             else %}  {{ interface.name }} * {{ mac.address | lower }}  * -{% endif %} -{% endif %} -{% endfor %} +{%             endif %} +{%         endif %} +{%     endfor %}  {% endfor %} diff --git a/data/templates/accel-ppp/chap-secrets.tmpl b/data/templates/accel-ppp/chap-secrets.j2 index 6cace5401..cc3ddc28f 100644 --- a/data/templates/accel-ppp/chap-secrets.tmpl +++ b/data/templates/accel-ppp/chap-secrets.j2 @@ -1,10 +1,10 @@  # username  server  password  acceptable local IP addresses   shaper  {% for user in local_users %} -{% if user.state == 'enabled' %} -{% if user.upload and user.download %} +{%     if user.state == 'enabled' %} +{%         if user.upload and user.download %}  {{ "%-12s" | format(user.name) }} * {{ "%-16s" | format(user.password) }} {{ "%-16s" | format(user.ip) }} {{ user.download }}/{{ user.upload }} -{% else %} +{%         else %}  {{ "%-12s" | format(user.name) }} * {{ "%-16s" | format(user.password) }} {{ "%-16s" | format(user.ip) }} -{% endif %} -{% endif %} +{%         endif %} +{%     endif %}  {% endfor %} diff --git a/data/templates/accel-ppp/config_shaper_radius.j2 b/data/templates/accel-ppp/config_shaper_radius.j2 index c256647e4..942cdf132 100644 --- a/data/templates/accel-ppp/config_shaper_radius.j2 +++ b/data/templates/accel-ppp/config_shaper_radius.j2 @@ -6,5 +6,8 @@ attr={{ authentication.radius.rate_limit.attribute }}  {%         if authentication.radius.rate_limit.vendor is vyos_defined %}  vendor={{ authentication.radius.rate_limit.vendor }}  {%         endif %} +{%         if authentication.radius.rate_limit.multiplier is vyos_defined %} +rate-multiplier={{ authentication.radius.rate_limit.multiplier }} +{%         endif %}  {%     endif %}  {% endif %} diff --git a/data/templates/accel-ppp/ipoe.config.tmpl b/data/templates/accel-ppp/ipoe.config.j2 index 92c2d5715..3c0d47b27 100644 --- a/data/templates/accel-ppp/ipoe.config.tmpl +++ b/data/templates/accel-ppp/ipoe.config.j2 @@ -1,3 +1,4 @@ +{# j2lint: disable=operator-enclosed-by-spaces #}  ### generated by ipoe.py ###  [modules]  log_syslog @@ -24,45 +25,50 @@ level=5  [ipoe]  verbose=1  {% for interface in interfaces %} -{% if interface.vlan_mon %} -interface=re:{{ interface.name }}\.\d+,{% else %}interface={{ interface.name }},{% endif %}shared={{ interface.shared }},mode={{ interface.mode }},ifcfg={{ interface.ifcfg }}{{ ',range=' + interface.range if interface.range is defined and interface.range is not none }},start={{ interface.sess_start }},ipv6=1 +{%     set tmp = 'interface=' %} +{%     if interface.vlan_mon %} +{%         set tmp = tmp ~ 're:' ~ interface.name ~ '\.\d+' %} +{%     else %} +{%         set tmp = tmp ~ interface.name %} +{%     endif %} +{{ tmp }},shared={{ interface.shared }},mode={{ interface.mode }},ifcfg={{ interface.ifcfg }}{{ ',range=' ~ interface.range if interface.range is defined and interface.range is not none }},start={{ interface.sess_start }},ipv6=1  {% endfor %} -{%   if auth_mode == 'noauth' %} +{% if auth_mode == 'noauth' %}  noauth=1  {%     if client_named_ip_pool %} -{%       for pool in client_named_ip_pool %} -{%         if pool.subnet is defined  %} +{%         for pool in client_named_ip_pool %} +{%             if pool.subnet is defined  %}  ip-pool={{ pool.name }} -{%         endif %} -{%         if pool.gateway_address is defined %} +{%             endif %} +{%             if pool.gateway_address is defined %}  gw-ip-address={{ pool.gateway_address }}/{{ pool.subnet.split('/')[1] }} -{%         endif %} -{%       endfor%} +{%             endif %} +{%         endfor %}  {%     endif %} -{%   elif auth_mode == 'local' %} +{% elif auth_mode == 'local' %}  username=ifname  password=csid  {% endif %}  proxy-arp=1  {% for interface in interfaces %} -{% if (interface.shared == '0') and (interface.vlan_mon) %} +{%     if (interface.shared == '0') and (interface.vlan_mon) %}  vlan-mon={{ interface.name }},{{ interface.vlan_mon | join(',') }} -{% endif %} +{%     endif %}  {% endfor %}  {% if dnsv4 %}  [dns] -{% for dns in dnsv4 %} +{%     for dns in dnsv4 %}  dns{{ loop.index }}={{ dns }} -{% endfor %} +{%     endfor %}  {% endif %}  {% if dnsv6 %}  [ipv6-dns] -{% for dns in dnsv6 %} +{%     for dns in dnsv6 %}  {{ dns }} -{% endfor %} +{%     endfor %}  {% endif %}  [ipv6-nd] @@ -73,24 +79,24 @@ verbose=1  {% if client_named_ip_pool %}  [ip-pool] -{%   for pool in client_named_ip_pool %} -{%     if pool.subnet is defined  %} +{%     for pool in client_named_ip_pool %} +{%         if pool.subnet is defined  %}  {{ pool.subnet }},name={{ pool.name }} -{%     endif %} -{%     if pool.gateway_address is defined %} +{%         endif %} +{%         if pool.gateway_address is defined %}  gw-ip-address={{ pool.gateway_address }}/{{ pool.subnet.split('/')[1] }} -{%     endif %} -{%   endfor%} +{%         endif %} +{%     endfor %}  {% endif %}  {% if client_ipv6_pool %}  [ipv6-pool] -{% for p in client_ipv6_pool %} +{%     for p in client_ipv6_pool %}  {{ p.prefix }},{{ p.mask }} -{% endfor %} -{% for p in client_ipv6_delegate_prefix %} +{%     endfor %} +{%     for p in client_ipv6_delegate_prefix %}  delegate={{ p.prefix }},{{ p.mask }} -{% endfor %} +{%     endfor %}  {% endif %}  {% if auth_mode == 'local' %} @@ -99,39 +105,37 @@ chap-secrets={{ chap_secrets_file }}  {% elif auth_mode == 'radius' %}  [radius]  verbose=1 -{% for r in radius_server %} +{%     for r in radius_server %}  server={{ r.server }},{{ r.key }},auth-port={{ r.port }},acct-port={{ r.acct_port }},req-limit=0,fail-time={{ r.fail_time }} -{% endfor %} +{%     endfor %} -{% if radius_acct_inter_jitter %} +{%     if radius_acct_inter_jitter %}  acct-interim-jitter={{ radius_acct_inter_jitter }} -{% endif %} +{%     endif %}  acct-timeout={{ radius_acct_tmo }}  timeout={{ radius_timeout }}  max-try={{ radius_max_try }} -{% if radius_nas_id %} +{%     if radius_nas_id %}  nas-identifier={{ radius_nas_id }} -{% endif %} -{% if radius_nas_ip %} +{%     endif %} +{%     if radius_nas_ip %}  nas-ip-address={{ radius_nas_ip }} -{% endif %} -{% if radius_source_address %} +{%     endif %} +{%     if radius_source_address %}  bind={{ radius_source_address }} -{% endif %} - -{% if radius_dynamic_author %} +{%     endif %} +{%     if radius_dynamic_author %}  dae-server={{ radius_dynamic_author.server }}:{{ radius_dynamic_author.port }},{{ radius_dynamic_author.key }} -{% endif %} - -{% if radius_shaper_attr %} +{%     endif %} +{%     if radius_shaper_attr %}  [shaper]  verbose=1  attr={{ radius_shaper_attr }} -{% if radius_shaper_vendor %} +{%         if radius_shaper_vendor %}  vendor={{ radius_shaper_vendor }} -{% endif %} -{% endif %} +{%         endif %} +{%     endif %}  {% endif %}  [cli] diff --git a/data/templates/accel-ppp/l2tp.config.tmpl b/data/templates/accel-ppp/l2tp.config.j2 index 9fcda76d4..9eeaf7622 100644 --- a/data/templates/accel-ppp/l2tp.config.tmpl +++ b/data/templates/accel-ppp/l2tp.config.j2 @@ -3,9 +3,9 @@  log_syslog  l2tp  chap-secrets -{% for proto in auth_proto: %} -{{proto}} -{% endfor%} +{% for proto in auth_proto %} +{{ proto }} +{% endfor %}  {% if auth_mode == 'radius' %}  radius @@ -18,7 +18,7 @@ ipv6_nd  ipv6_dhcp  [core] -thread-count={{thread_cnt}} +thread-count={{ thread_cnt }}  [log]  syslog=accel-l2tp,daemon @@ -27,23 +27,23 @@ level=5  {% if dnsv4 %}  [dns] -{% for dns in dnsv4 %} +{%     for dns in dnsv4 %}  dns{{ loop.index }}={{ dns }} -{% endfor %} +{%     endfor %}  {% endif %}  {% if dnsv6 %}  [ipv6-dns] -{% for dns in dnsv6 %} +{%     for dns in dnsv6 %}  {{ dns }} -{% endfor %} +{%     endfor %}  {% endif %}  {% if wins %}  [wins] -{% for server in wins %} +{%     for server in wins %}  wins{{ loop.index }}={{ server }} -{% endfor %} +{%     endfor %}  {% endif %}  [l2tp] @@ -66,14 +66,14 @@ host-name={{ lns_host_name }}  {% if client_ip_pool or client_ip_subnets %}  [ip-pool] -{% if client_ip_pool %} +{%     if client_ip_pool %}  {{ client_ip_pool }} -{% endif %} -{% if client_ip_subnets %} -{% for sn in client_ip_subnets %} -{{sn}} -{% endfor %} -{% endif %} +{%     endif %} +{%     if client_ip_subnets %} +{%         for sn in client_ip_subnets %} +{{ sn }} +{%         endfor %} +{%     endif %}  {% endif %}  {% if gateway_address %}  gw-ip-address={{ gateway_address }} @@ -85,27 +85,24 @@ chap-secrets={{ chap_secrets_file }}  {% elif auth_mode == 'radius' %}  [radius]  verbose=1 -{% for r in radius_server %} +{%     for r in radius_server %}  server={{ r.server }},{{ r.key }},auth-port={{ r.port }},acct-port={{ r.acct_port }},req-limit=0,fail-time={{ r.fail_time }} -{% endfor %} - -{% if radius_acct_inter_jitter %} +{%     endfor %} +{%     if radius_acct_inter_jitter %}  acct-interim-jitter={{ radius_acct_inter_jitter }} -{% endif %} - +{%     endif %}  acct-timeout={{ radius_acct_tmo }}  timeout={{ radius_timeout }}  max-try={{ radius_max_try }} - -{% if radius_nas_id %} +{%     if radius_nas_id %}  nas-identifier={{ radius_nas_id }} -{% endif %} -{% if radius_nas_ip %} +{%     endif %} +{%     if radius_nas_ip %}  nas-ip-address={{ radius_nas_ip }} -{% endif %} -{% if radius_source_address %} +{%     endif %} +{%     if radius_source_address %}  bind={{ radius_source_address }} -{% endif %} +{%     endif %}  {% endif %}  {% if gateway_address %}  gw-ip-address={{ gateway_address }} @@ -128,12 +125,12 @@ ipv6=allow  {% if client_ipv6_pool %}  [ipv6-pool] -{% for p in client_ipv6_pool %} +{%     for p in client_ipv6_pool %}  {{ p.prefix }},{{ p.mask }} -{% endfor %} -{% for p in client_ipv6_delegate_prefix %} +{%     endfor %} +{%     for p in client_ipv6_delegate_prefix %}  delegate={{ p.prefix }},{{ p.mask }} -{% endfor %} +{%     endfor %}  {% endif %}  {% if client_ipv6_delegate_prefix %} @@ -145,9 +142,9 @@ verbose=1  [shaper]  verbose=1  attr={{ radius_shaper_attr }} -{% if radius_shaper_vendor %} +{%     if radius_shaper_vendor %}  vendor={{ radius_shaper_vendor }} -{% endif %} +{%     endif %}  {% endif %}  [cli] diff --git a/data/templates/accel-ppp/pppoe.config.tmpl b/data/templates/accel-ppp/pppoe.config.j2 index 81b98cc81..0a92e2d54 100644 --- a/data/templates/accel-ppp/pppoe.config.tmpl +++ b/data/templates/accel-ppp/pppoe.config.j2 @@ -49,9 +49,9 @@ disable  {% if wins_server is vyos_defined %}  [wins] -{%   for server in wins_server %} +{%     for server in wins_server %}  wins{{ loop.index }}={{ server }} -{%   endfor %} +{%     endfor %}  {% endif %}  {# Common chap-secrets and RADIUS server/option definitions #} @@ -85,12 +85,12 @@ ipv4={{ ppp_options.ipv4 }}  {# IPv6 #}  {% if ppp_options.ipv6 is vyos_defined %}  ipv6={{ ppp_options.ipv6 }} -{%   if ppp_options.ipv6_intf_id is vyos_defined %} +{%     if ppp_options.ipv6_intf_id is vyos_defined %}  ipv6-intf-id={{ ppp_options.ipv6_intf_id }} -{%   endif %} -{%   if ppp_options.ipv6_peer_intf_id is vyos_defined %} +{%     endif %} +{%     if ppp_options.ipv6_peer_intf_id is vyos_defined %}  ipv6-peer-intf-id={{ ppp_options.ipv6_peer_intf_id }} -{%   endif %} +{%     endif %}  ipv6-accept-peer-intf-id={{ "1" if ppp_options.ipv6_accept_peer_intf_id is vyos_defined else "0" }}  {% endif %}  {# MTU #} @@ -104,23 +104,23 @@ verbose=1  ac-name={{ access_concentrator }}  {% if interface is vyos_defined %} -{%   for iface, iface_config in interface.items() %} -{%     if iface_config.vlan_id is not vyos_defined and iface_config.vlan_range is not vyos_defined %} +{%     for iface, iface_config in interface.items() %} +{%         if iface_config.vlan_id is not vyos_defined and iface_config.vlan_range is not vyos_defined %}  interface={{ iface }} -{%     endif %} -{%     if iface_config.vlan_range is vyos_defined %} -{%       for regex in iface_config.regex %} +{%         endif %} +{%         if iface_config.vlan_range is vyos_defined %} +{%             for regex in iface_config.regex %}  interface=re:^{{ iface | replace('.', '\\.') }}\.({{ regex }})$ -{%       endfor %} +{%             endfor %}  vlan-mon={{ iface }},{{ iface_config.vlan_range | join(',') }} -{%     endif %} -{%     if iface_config.vlan_id is vyos_defined %} -{%       for vlan in iface_config.vlan_id %} +{%         endif %} +{%         if iface_config.vlan_id is vyos_defined %} +{%             for vlan in iface_config.vlan_id %}  vlan-mon={{ iface }},{{ vlan }}  interface=re:^{{ iface | replace('.', '\\.') }}\.{{ vlan }}$ -{%       endfor %} -{%     endif %} -{%   endfor %} +{%             endfor %} +{%         endif %} +{%     endfor %}  {% endif %}  {% if service_name %} @@ -128,14 +128,14 @@ service-name={{ service_name | join(',') }}  {% endif %}  {% if pado_delay %} -{%   set pado_delay_param = namespace(value='0') %} -{%   for delay in pado_delay|sort(attribute='0') %} -{%     if not loop.last %} -{%       set pado_delay_param.value = pado_delay_param.value + ',' + delay + ':' + pado_delay[delay].sessions %} -{%     else %} -{%       set pado_delay_param.value = pado_delay_param.value + ',-1:' + pado_delay[delay].sessions %} -{%     endif %} -{%   endfor %} +{%     set pado_delay_param = namespace(value='0') %} +{%     for delay in pado_delay | sort(attribute='0') %} +{%         if not loop.last %} +{%             set pado_delay_param.value = pado_delay_param.value + ',' + delay + ':' + pado_delay[delay].sessions %} +{%         else %} +{%             set pado_delay_param.value = pado_delay_param.value + ',-1:' + pado_delay[delay].sessions %} +{%         endif %} +{%     endfor %}  pado-delay={{ pado_delay_param.value }}  {% endif %}  {% if authentication.radius.called_sid_format is vyos_defined %} @@ -144,15 +144,15 @@ called-sid={{ authentication.radius.called_sid_format }}  {% if limits is vyos_defined %}  [connlimit] -{%   if limits.connection_limit is vyos_defined %} +{%     if limits.connection_limit is vyos_defined %}  limit={{ limits.connection_limit }} -{%   endif %} -{%   if limits.burst is vyos_defined %} +{%     endif %} +{%     if limits.burst is vyos_defined %}  burst={{ limits.burst }} -{%   endif %} -{%   if limits.timeout is vyos_defined %} +{%     endif %} +{%     if limits.timeout is vyos_defined %}  timeout={{ limits.timeout }} -{%   endif %} +{%     endif %}  {% endif %}  {# Common RADIUS shaper configuration #} @@ -162,10 +162,10 @@ timeout={{ limits.timeout }}  [pppd-compat]  verbose=1  radattr-prefix=/run/accel-pppd/radattr -{% set script_name = {'on_up': 'ip-up', 'on_down': 'ip-down', 'on_change':'ip-change', 'on_pre_up':'ip-pre-up'} %} -{%   for script in extended_scripts %} +{%     set script_name = {'on_up': 'ip-up', 'on_down': 'ip-down', 'on_change':'ip-change', 'on_pre_up':'ip-pre-up'} %} +{%     for script in extended_scripts %}  {{ script_name[script] }}={{ extended_scripts[script] }} -{%   endfor %} +{%     endfor %}  {% endif %}  [cli] diff --git a/data/templates/accel-ppp/pptp.config.tmpl b/data/templates/accel-ppp/pptp.config.j2 index 3cfc4a906..cc1a45d6b 100644 --- a/data/templates/accel-ppp/pptp.config.tmpl +++ b/data/templates/accel-ppp/pptp.config.j2 @@ -10,7 +10,7 @@ radius  {% endif %}  ippool  {% for proto in auth_proto %} -{{proto}} +{{ proto }}  {% endfor %}  [core] @@ -23,16 +23,16 @@ level=5  {% if dnsv4 %}  [dns] -{% for dns in dnsv4 %} +{%     for dns in dnsv4 %}  dns{{ loop.index }}={{ dns }} -{% endfor %} +{%     endfor %}  {% endif %}  {% if wins %}  [wins] -{% for server in wins %} +{%     for server in wins %}  wins{{ loop.index }}={{ server }} -{% endfor %} +{%     endfor %}  {% endif %} @@ -42,7 +42,7 @@ ifname=pptp%d  bind={{ outside_addr }}  {% endif %}  verbose=1 -ppp-max-mtu={{mtu}} +ppp-max-mtu={{ mtu }}  mppe={{ ppp_mppe }}  echo-interval=10  echo-failure=3 @@ -66,27 +66,27 @@ chap-secrets={{ chap_secrets_file }}  {% elif auth_mode == 'radius' %}  [radius]  verbose=1 -{% for r in radius_server %} +{%     for r in radius_server %}  server={{ r.server }},{{ r.key }},auth-port={{ r.port }},acct-port={{ r.acct_port }},req-limit=0,fail-time={{ r.fail_time }} -{% endfor %} +{%     endfor %} -{% if radius_acct_inter_jitter %} +{%     if radius_acct_inter_jitter %}  acct-interim-jitter={{ radius_acct_inter_jitter }} -{% endif %} +{%     endif %}  acct-timeout={{ radius_acct_tmo }}  timeout={{ radius_timeout }}  max-try={{ radius_max_try }} -{% if radius_nas_id %} +{%     if radius_nas_id %}  nas-identifier={{ radius_nas_id }} -{% endif %} -{% if radius_nas_ip %} +{%     endif %} +{%     if radius_nas_ip %}  nas-ip-address={{ radius_nas_ip }} -{% endif %} -{% if radius_source_address %} +{%     endif %} +{%     if radius_source_address %}  bind={{ radius_source_address }} -{% endif %} +{%     endif %}  {% endif %}  {# Both chap-secrets and radius block required the gw-ip-address #}  {% if gw_ip is defined and gw_ip is not none %} diff --git a/data/templates/accel-ppp/sstp.config.tmpl b/data/templates/accel-ppp/sstp.config.j2 index 5c6f19306..5c6f19306 100644 --- a/data/templates/accel-ppp/sstp.config.tmpl +++ b/data/templates/accel-ppp/sstp.config.j2 diff --git a/data/templates/bcast-relay/udp-broadcast-relay.tmpl b/data/templates/bcast-relay/udp-broadcast-relay.j2 index 75740e04c..75740e04c 100644 --- a/data/templates/bcast-relay/udp-broadcast-relay.tmpl +++ b/data/templates/bcast-relay/udp-broadcast-relay.j2 diff --git a/data/templates/conntrack/nftables-ct.j2 b/data/templates/conntrack/nftables-ct.j2 new file mode 100644 index 000000000..16a03fc6e --- /dev/null +++ b/data/templates/conntrack/nftables-ct.j2 @@ -0,0 +1,48 @@ +#!/usr/sbin/nft -f + +{% set nft_ct_ignore_name = 'VYOS_CT_IGNORE' %} +{% set nft_ct_timeout_name = 'VYOS_CT_TIMEOUT' %} + +# we first flush all chains and render the content from scratch - this makes +# any delta check obsolete +flush chain raw {{ nft_ct_ignore_name }} +flush chain raw {{ nft_ct_timeout_name }} + +table raw { +    chain {{ nft_ct_ignore_name }} { +{% if ignore.rule is vyos_defined %} +{%     for rule, rule_config in ignore.rule.items() %} +        # rule-{{ rule }} {{ '- ' ~ rule_config.description if rule_config.description is vyos_defined }} +{%         set nft_command = '' %} +{%         if rule_config.inbound_interface is vyos_defined %} +{%             set nft_command = nft_command ~ ' iifname ' ~ rule_config.inbound_interface %} +{%         endif %} +{%         if rule_config.protocol is vyos_defined %} +{%             set nft_command = nft_command ~ ' ip protocol ' ~ rule_config.protocol %} +{%         endif %} +{%         if rule_config.destination.address is vyos_defined %} +{%             set nft_command = nft_command ~ ' ip daddr ' ~ rule_config.destination.address %} +{%         endif %} +{%         if rule_config.destination.port is vyos_defined %} +{%             set nft_command = nft_command ~ ' ' ~ rule_config.protocol ~ ' dport { ' ~ rule_config.destination.port ~ ' }' %} +{%         endif %} +{%         if rule_config.source.address is vyos_defined %} +{%             set nft_command = nft_command ~ ' ip saddr ' ~ rule_config.source.address %} +{%         endif %} +{%         if rule_config.source.port is vyos_defined %} +{%             set nft_command = nft_command ~ ' ' ~ rule_config.protocol ~ ' sport { ' ~ rule_config.source.port ~ ' }' %} +{%         endif %} +       {{ nft_command }} counter notrack comment ignore-{{ rule }} +{%     endfor %} +{% endif %} +        return +    } +    chain {{ nft_ct_timeout_name }} { +{% if timeout.custom.rule is vyos_defined %} +{%     for rule, rule_config in timeout.custom.rule.items() %} +        # rule-{{ rule }} {{ '- ' ~ rule_config.description if rule_config.description is vyos_defined }} +{%     endfor %} +{% endif %} +        return +    } +} diff --git a/data/templates/conntrack/nftables-ct.tmpl b/data/templates/conntrack/nftables-ct.tmpl deleted file mode 100644 index 569e73df1..000000000 --- a/data/templates/conntrack/nftables-ct.tmpl +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/sbin/nft -f - -{% set nft_ct_ignore_name = 'VYOS_CT_IGNORE' %} -{% set nft_ct_timeout_name = 'VYOS_CT_TIMEOUT' %} - -# we first flush all chains and render the content from scratch - this makes -# any delta check obsolete -flush chain raw {{ nft_ct_ignore_name }} -flush chain raw {{ nft_ct_timeout_name }} - -table raw { -    chain {{ nft_ct_ignore_name }} { -{% if ignore.rule is vyos_defined %} -{%   for rule, rule_config in ignore.rule.items() %} -        # rule-{{ rule }} {{ '- ' ~ rule_config.description if rule_config.description is vyos_defined }} -{%     set nft_command = '' %} -{%     if rule_config.inbound_interface is vyos_defined %} -{%       set nft_command = nft_command ~ ' iifname ' ~ rule_config.inbound_interface %} -{%     endif %} -{%     if rule_config.protocol is vyos_defined %} -{%       set nft_command = nft_command ~ ' ip protocol ' ~ rule_config.protocol %} -{%     endif %} -{%     if rule_config.destination.address is vyos_defined %} -{%       set nft_command = nft_command ~ ' ip daddr ' ~ rule_config.destination.address %} -{%     endif %} -{%     if rule_config.destination.port is vyos_defined %} -{%       set nft_command = nft_command ~ ' ' ~ rule_config.protocol ~ ' dport { ' ~ rule_config.destination.port ~ ' }' %} -{%     endif %} -{%     if rule_config.source.address is vyos_defined %} -{%       set nft_command = nft_command ~ ' ip saddr ' ~ rule_config.source.address %} -{%     endif %} -{%     if rule_config.source.port is vyos_defined %} -{%       set nft_command = nft_command ~ ' ' ~ rule_config.protocol ~ ' sport { ' ~ rule_config.source.port ~ ' }' %} -{%     endif %} -       {{ nft_command }} counter notrack comment ignore-{{ rule }} -{%   endfor %} -{% endif %} -        return -    } -    chain {{ nft_ct_timeout_name }} { -{% if timeout.custom.rule is vyos_defined %} -{%   for rule, rule_config in timeout.custom.rule.items() %} -        # rule-{{ rule }} {{ '- ' ~ rule_config.description if rule_config.description is vyos_defined }} -{%   endfor %} -{% endif %} -        return -    } -} diff --git a/data/templates/conntrack/sysctl.conf.tmpl b/data/templates/conntrack/sysctl.conf.j2 index 075402c04..075402c04 100644 --- a/data/templates/conntrack/sysctl.conf.tmpl +++ b/data/templates/conntrack/sysctl.conf.j2 diff --git a/data/templates/conntrack/vyos_nf_conntrack.conf.tmpl b/data/templates/conntrack/vyos_nf_conntrack.conf.j2 index 111459485..111459485 100644 --- a/data/templates/conntrack/vyos_nf_conntrack.conf.tmpl +++ b/data/templates/conntrack/vyos_nf_conntrack.conf.j2 diff --git a/data/templates/conntrackd/conntrackd.conf.tmpl b/data/templates/conntrackd/conntrackd.conf.j2 index 80e7254a0..66024869d 100644 --- a/data/templates/conntrackd/conntrackd.conf.tmpl +++ b/data/templates/conntrackd/conntrackd.conf.j2 @@ -6,11 +6,11 @@ Sync {          DisableExternalCache {{ 'on' if disable_external_cache is vyos_defined else 'off' }}      }  {% for iface, iface_config in interface.items() %} -{%   if iface_config.peer is vyos_defined %} +{%     if iface_config.peer is vyos_defined %}      UDP { -{%     if listen_address is vyos_defined %} +{%         if listen_address is vyos_defined %}          IPv4_address {{ listen_address }} -{%     endif %} +{%         endif %}          IPv4_Destination_Address {{ iface_config.peer }}          Port {{ iface_config.port if iface_config.port is vyos_defined else '3780' }}          Interface {{ iface }} @@ -18,9 +18,9 @@ Sync {          RcvSocketBuffer {{ sync_queue_size | int *1024 *1024 }}          Checksum on      } -{%   else %} +{%     else %}      Multicast { -{%     set ip_address = iface | get_ipv4 %} +{%         set ip_address = iface | get_ipv4 %}          IPv4_address {{ mcast_group }}          Group {{ iface_config.port if iface_config.port is vyos_defined else '3780' }}          IPv4_interface {{ ip_address[0] | ip_from_cidr }} @@ -29,19 +29,19 @@ Sync {          RcvSocketBuffer {{ sync_queue_size | int *1024 *1024 }}          Checksum on      } -{%   endif %} +{%     endif %}  {% endfor %}  {% if expect_sync is vyos_defined %}      Options { -{%   if 'all' in expect_sync %} +{%     if 'all' in expect_sync %}          ExpectationSync on -{%   else %} +{%     else %}          ExpectationSync { -{%     for protocol in expect_sync %} +{%         for protocol in expect_sync %}              {{ protocol }} -{%     endfor %} +{%         endfor %}          } -{%   endif %} +{%     endif %}      }  {% endif %}  } @@ -85,27 +85,27 @@ General {      NetlinkEventsReliable on  {% if ignore_address is vyos_defined or accept_protocol is vyos_defined %}      Filter From Userspace { -{%   if ignore_address is vyos_defined %} +{%     if ignore_address is vyos_defined %}          Address Ignore { -{%     for address in ignore_address if address | is_ipv4 %} +{%         for address in ignore_address if address | is_ipv4 %}              IPv4_address {{ address }} -{%     endfor %} -{%     for address in ignore_address if address | is_ipv6 %} +{%         endfor %} +{%         for address in ignore_address if address | is_ipv6 %}              IPv6_address {{ address }} -{%     endfor %} +{%         endfor %}          } -{%   endif %} -{%   if accept_protocol is vyos_defined %} +{%     endif %} +{%     if accept_protocol is vyos_defined %}          Protocol Accept { -{%     for protocol in accept_protocol %} -{%       if protocol == 'icmp6' %} +{%         for protocol in accept_protocol %} +{%             if protocol == 'icmp6' %}              IPv6-ICMP -{%       else %} +{%             else %}              {{ protocol | upper }} -{%       endif %} -{%     endfor %} +{%             endif %} +{%         endfor %}          } -{%   endif %} +{%     endif %}      }  {% endif %}  } diff --git a/data/templates/conntrackd/conntrackd.op-mode.j2 b/data/templates/conntrackd/conntrackd.op-mode.j2 new file mode 100644 index 000000000..82f7e2859 --- /dev/null +++ b/data/templates/conntrackd/conntrackd.op-mode.j2 @@ -0,0 +1,13 @@ +Source                                           Destination                                      Protocol +{% for parsed in data if parsed.flow.meta is vyos_defined %} +{%     for key in parsed.flow.meta %} +{%         if key['@direction'] == 'original' %} +{%             set saddr    = key.layer3.src | bracketize_ipv6 %} +{%             set sport    = key.layer4.sport %} +{%             set daddr    = key.layer3.dst | bracketize_ipv6 %} +{%             set dport    = key.layer4.dport %} +{%             set protocol = key.layer4['@protoname'] %} +{{ "%-48s" | format(saddr ~ ':' ~ sport) }} {{ "%-48s" | format(daddr ~ ':' ~ dport) }} {{ protocol }} +{%         endif %} +{%     endfor %} +{% endfor %} diff --git a/data/templates/conntrackd/conntrackd.op-mode.tmpl b/data/templates/conntrackd/conntrackd.op-mode.tmpl deleted file mode 100644 index c3f6911ce..000000000 --- a/data/templates/conntrackd/conntrackd.op-mode.tmpl +++ /dev/null @@ -1,13 +0,0 @@ -Source                                           Destination                                      Protocol -{% for parsed in data if parsed.flow.meta is vyos_defined %} -{%   for key in parsed.flow.meta %} -{%     if key['@direction'] == 'original' %} -{%       set saddr    = key.layer3.src | bracketize_ipv6 %} -{%       set sport    = key.layer4.sport %} -{%       set daddr    = key.layer3.dst | bracketize_ipv6 %} -{%       set dport    = key.layer4.dport %} -{%       set protocol = key.layer4['@protoname'] %} -{{ "%-48s" | format(saddr ~ ':' ~ sport) }} {{ "%-48s" | format(daddr ~ ':' ~ dport) }} {{ protocol }} -{%     endif %} -{%   endfor %} -{% endfor %} diff --git a/data/templates/conserver/conserver.conf.tmpl b/data/templates/conserver/conserver.conf.j2 index 4e7b5d8d7..1823657d7 100644 --- a/data/templates/conserver/conserver.conf.tmpl +++ b/data/templates/conserver/conserver.conf.j2 @@ -17,7 +17,7 @@ default * {  ##  {% for key, value in device.items() %}  {#   Depending on our USB serial console we could require a path adjustment #} -{%   set path = '/dev' if key.startswith('ttyS') else '/dev/serial/by-bus' %} +{%     set path = '/dev' if key.startswith('ttyS') else '/dev/serial/by-bus' %}  console {{ key }} {      master localhost;      type device; diff --git a/data/templates/conserver/dropbear@.service.tmpl b/data/templates/conserver/dropbear@.service.j2 index e355dab43..e355dab43 100644 --- a/data/templates/conserver/dropbear@.service.tmpl +++ b/data/templates/conserver/dropbear@.service.j2 diff --git a/data/templates/containers/registries.conf.j2 b/data/templates/container/registries.conf.j2 index 4057bb452..2e86466a1 100644 --- a/data/templates/containers/registries.conf.j2 +++ b/data/templates/container/registries.conf.j2 @@ -1,4 +1,4 @@ -### Autogenerated by /usr/libexec/vyos/conf_mode/containers.py ### +### Autogenerated by container.py ###  # For more information on this configuration file, see containers-registries.conf(5).  # diff --git a/data/templates/container/storage.conf.j2 b/data/templates/container/storage.conf.j2 new file mode 100644 index 000000000..665f9bf95 --- /dev/null +++ b/data/templates/container/storage.conf.j2 @@ -0,0 +1,4 @@ +### Autogenerated by container.py ### +[storage] +  driver = "vfs" +  graphroot = "/usr/lib/live/mount/persistence/container/storage" diff --git a/data/templates/containers/storage.conf.j2 b/data/templates/containers/storage.conf.j2 deleted file mode 100644 index 3a69b7252..000000000 --- a/data/templates/containers/storage.conf.j2 +++ /dev/null @@ -1,5 +0,0 @@ -### Autogenerated by /usr/libexec/vyos/conf_mode/containers.py ### - -[storage] -  driver = "vfs" -  graphroot = "/config/containers/storage" diff --git a/data/templates/firewall/nftables-defines.j2 b/data/templates/firewall/nftables-defines.j2 new file mode 100644 index 000000000..4fa92f2e3 --- /dev/null +++ b/data/templates/firewall/nftables-defines.j2 @@ -0,0 +1,32 @@ +{% if group is vyos_defined %} +{%     if group.address_group is vyos_defined %} +{%         for group_name, group_conf in group.address_group.items() %} +define A_{{ group_name }} = { {{ group_conf.address | join(",") }} } +{%         endfor %} +{%     endif %} +{%     if group.ipv6_address_group is vyos_defined %} +{%         for group_name, group_conf in group.ipv6_address_group.items() %} +define A6_{{ group_name }} = { {{ group_conf.address | join(",") }} } +{%         endfor %} +{%     endif %} +{%     if group.mac_group is vyos_defined %} +{%         for group_name, group_conf in group.mac_group.items() %} +define M_{{ group_name }} = { {{ group_conf.mac_address | join(",") }} } +{%         endfor %} +{%     endif %} +{%     if group.network_group is vyos_defined %} +{%         for group_name, group_conf in group.network_group.items() %} +define N_{{ group_name }} = { {{ group_conf.network | join(",") }} } +{%         endfor %} +{%     endif %} +{%     if group.ipv6_network_group is vyos_defined %} +{%         for group_name, group_conf in group.ipv6_network_group.items() %} +define N6_{{ group_name }} = { {{ group_conf.network | join(",") }} } +{%         endfor %} +{%     endif %} +{%     if group.port_group is vyos_defined %} +{%         for group_name, group_conf in group.port_group.items() %} +define P_{{ group_name }} = { {{ group_conf.port | join(",") }} } +{%         endfor %} +{%     endif %} +{% endif %}
\ No newline at end of file diff --git a/data/templates/firewall/nftables-defines.tmpl b/data/templates/firewall/nftables-defines.tmpl deleted file mode 100644 index 66d31093b..000000000 --- a/data/templates/firewall/nftables-defines.tmpl +++ /dev/null @@ -1,32 +0,0 @@ -{% if group is vyos_defined %} -{%   if group.address_group is vyos_defined %} -{%     for group_name, group_conf in group.address_group.items() %} -define A_{{ group_name }} = { {{ group_conf.address | join(",") }} } -{%     endfor %} -{%   endif %} -{%   if group.ipv6_address_group is vyos_defined %} -{%     for group_name, group_conf in group.ipv6_address_group.items() %} -define A6_{{ group_name }} = { {{ group_conf.address | join(",") }} } -{%     endfor %} -{%   endif %} -{%   if group.mac_group is vyos_defined %} -{%     for group_name, group_conf in group.mac_group.items() %} -define M_{{ group_name }} = { {{ group_conf.mac_address | join(",") }} } -{%     endfor %} -{%   endif %} -{%   if group.network_group is vyos_defined %} -{%     for group_name, group_conf in group.network_group.items() %} -define N_{{ group_name }} = { {{ group_conf.network | join(",") }} } -{%     endfor %} -{%   endif %} -{%   if group.ipv6_network_group is vyos_defined %} -{%     for group_name, group_conf in group.ipv6_network_group.items() %} -define N6_{{ group_name }} = { {{ group_conf.network | join(",") }} } -{%     endfor %} -{%   endif %} -{%   if group.port_group is vyos_defined %} -{%     for group_name, group_conf in group.port_group.items() %} -define P_{{ group_name }} = { {{ group_conf.port | join(",") }} } -{%     endfor %} -{%   endif %} -{% endif %}
\ No newline at end of file diff --git a/data/templates/firewall/nftables-nat.tmpl b/data/templates/firewall/nftables-nat.j2 index 63aa48c77..1481e9104 100644 --- a/data/templates/firewall/nftables-nat.tmpl +++ b/data/templates/firewall/nftables-nat.j2 @@ -1,141 +1,141 @@  #!/usr/sbin/nft -f  {% macro nat_rule(rule, config, chain) %} -{%   set comment  = '' %} -{%   set base_log = '' %} -{%   set src_addr  = 'ip saddr ' ~ config.source.address.replace('!','!= ') if config.source.address is vyos_defined %} -{%   set dst_addr  = 'ip daddr ' ~ config.destination.address.replace('!','!= ') if config.destination.address is vyos_defined %} -{#   negated port groups need special treatment, move != in front of { } group #} -{%   if config.source.port is vyos_defined and config.source.port.startswith('!') %} +{% set comment  = '' %} +{% set base_log = '' %} +{% set src_addr  = 'ip saddr ' ~ config.source.address.replace('!','!= ') if config.source.address is vyos_defined %} +{% set dst_addr  = 'ip daddr ' ~ config.destination.address.replace('!','!= ') if config.destination.address is vyos_defined %} +{# negated port groups need special treatment, move != in front of { } group #} +{% if config.source.port is vyos_defined and config.source.port.startswith('!') %}  {%     set src_port  = 'sport != { ' ~ config.source.port.replace('!','') ~ ' }' %} -{%   else %} +{% else %}  {%     set src_port  = 'sport { ' ~ config.source.port ~ ' }' if config.source.port is vyos_defined %} -{%   endif %} -{#   negated port groups need special treatment, move != in front of { } group #} -{%   if config.destination.port is vyos_defined and config.destination.port.startswith('!') %} +{% endif %} +{# negated port groups need special treatment, move != in front of { } group #} +{% if config.destination.port is vyos_defined and config.destination.port.startswith('!') %}  {%     set dst_port  = 'dport != { ' ~ config.destination.port.replace('!','') ~ ' }' %} -{%   else %} +{% else %}  {%     set dst_port  = 'dport { ' ~ config.destination.port ~ ' }' if config.destination.port is vyos_defined %} -{%   endif %} -{%   if chain is vyos_defined('PREROUTING') %} +{% endif %} +{% if chain is vyos_defined('PREROUTING') %}  {%     set comment   = 'DST-NAT-' ~ rule %}  {%     set base_log  = '[NAT-DST-' ~ rule %}  {%     set interface = ' iifname "' ~ config.inbound_interface ~ '"' if config.inbound_interface is vyos_defined and config.inbound_interface is not vyos_defined('any') else '' %}  {%     if config.translation.address is vyos_defined %} -{#       support 1:1 network translation #} -{%       if config.translation.address | is_ip_network %} -{%         set trns_addr = 'dnat ip prefix to ip daddr map { ' ~ config.destination.address ~ ' : ' ~ config.translation.address ~ ' }' %} -{#         we can now clear out the dst_addr part as it's already covered in aboves map #} -{%         set dst_addr  = '' %} -{%       else %} -{%         set trns_addr = 'dnat to ' ~ config.translation.address %} -{%       endif %} +{#         support 1:1 network translation #} +{%         if config.translation.address | is_ip_network %} +{%             set trns_addr = 'dnat ip prefix to ip daddr map { ' ~ config.destination.address ~ ' : ' ~ config.translation.address ~ ' }' %} +{#             we can now clear out the dst_addr part as it's already covered in aboves map #} +{%             set dst_addr  = '' %} +{%         else %} +{%             set trns_addr = 'dnat to ' ~ config.translation.address %} +{%         endif %}  {%     endif %} -{%   elif chain is vyos_defined('POSTROUTING') %} +{% elif chain is vyos_defined('POSTROUTING') %}  {%     set comment   = 'SRC-NAT-' ~ rule %}  {%     set base_log  = '[NAT-SRC-' ~ rule %}  {%     set interface = ' oifname "' ~ config.outbound_interface ~ '"' if config.outbound_interface is vyos_defined and config.outbound_interface is not vyos_defined('any') else '' %}  {%     if config.translation.address is vyos_defined %} -{%       if config.translation.address is vyos_defined('masquerade') %} -{%         set trns_addr = config.translation.address %} -{%         if config.translation.port is vyos_defined %} -{%           set trns_addr = trns_addr ~ ' to ' %} +{%         if config.translation.address is vyos_defined('masquerade') %} +{%             set trns_addr = config.translation.address %} +{%             if config.translation.port is vyos_defined %} +{%                 set trns_addr = trns_addr ~ ' to ' %} +{%             endif %} +{#         support 1:1 network translation #} +{%         elif config.translation.address | is_ip_network %} +{%             set trns_addr = 'snat ip prefix to ip saddr map { ' ~ config.source.address ~ ' : ' ~ config.translation.address ~ ' }' %} +{#             we can now clear out the src_addr part as it's already covered in aboves map #} +{%             set src_addr  = '' %} +{%         else %} +{%             set trns_addr = 'snat to ' ~ config.translation.address %}  {%         endif %} -{#       support 1:1 network translation #} -{%       elif config.translation.address | is_ip_network %} -{%         set trns_addr = 'snat ip prefix to ip saddr map { ' ~ config.source.address ~ ' : ' ~ config.translation.address ~ ' }' %} -{#         we can now clear out the src_addr part as it's already covered in aboves map #} -{%         set src_addr  = '' %} -{%       else %} -{%         set trns_addr = 'snat to ' ~ config.translation.address %} -{%       endif %}  {%     endif %} -{%   endif %} -{%   set trns_port = ':' ~ config.translation.port if config.translation.port is vyos_defined %} -{#   protocol has a default value thus it is always present #} -{%   if config.protocol is vyos_defined('tcp_udp') %} +{% endif %} +{% set trns_port = ':' ~ config.translation.port if config.translation.port is vyos_defined %} +{# protocol has a default value thus it is always present #} +{% if config.protocol is vyos_defined('tcp_udp') %}  {%     set protocol  = 'tcp' %}  {%     set comment   = comment ~ ' tcp_udp' %} -{%   else %} +{% else %}  {%     set protocol  = config.protocol %} -{%   endif %} -{%   if config.log is vyos_defined %} +{% endif %} +{% if config.log is vyos_defined %}  {%     if config.exclude is vyos_defined %} -{%       set log = base_log ~ '-EXCL]' %} +{%         set log = base_log ~ '-EXCL]' %}  {%     elif config.translation.address is vyos_defined('masquerade') %} -{%       set log = base_log +'-MASQ]' %} +{%         set log = base_log ~ '-MASQ]' %}  {%     else %} -{%       set log = base_log ~ ']' %} +{%         set log = base_log ~ ']' %}  {%     endif %} -{%   endif %} -{%   if config.exclude is vyos_defined %} +{% endif %} +{% if config.exclude is vyos_defined %}  {#     rule has been marked as 'exclude' thus we simply return here #}  {%     set trns_addr = 'return' %}  {%     set trns_port = '' %} -{%   endif %} -{#   T1083: NAT address and port translation options #} -{%   if config.translation.options is vyos_defined %} +{% endif %} +{# T1083: NAT address and port translation options #} +{% if config.translation.options is vyos_defined %}  {%     if config.translation.options.address_mapping is vyos_defined('persistent') %}  {%         set trns_opts_addr  = 'persistent' %}  {%     endif %}  {%     if config.translation.options.port_mapping is vyos_defined('random') %} -{%       set trns_opts_port  = 'random' %} +{%         set trns_opts_port  = 'random' %}  {%     elif config.translation.options.port_mapping is vyos_defined('fully-random') %} -{%       set trns_opts_port  = 'fully-random' %} +{%         set trns_opts_port  = 'fully-random' %}  {%     endif %} -{%   endif %} -{%   if trns_opts_addr is vyos_defined and trns_opts_port is vyos_defined %} +{% endif %} +{% if trns_opts_addr is vyos_defined and trns_opts_port is vyos_defined %}  {%     set trns_opts  = trns_opts_addr ~ ',' ~ trns_opts_port %} -{%   elif trns_opts_addr is vyos_defined %} +{% elif trns_opts_addr is vyos_defined %}  {%     set trns_opts  = trns_opts_addr %} -{%   elif trns_opts_port is vyos_defined %} +{% elif trns_opts_port is vyos_defined %}  {%     set trns_opts  = trns_opts_port %} -{%   endif %} -{%   set output = 'add rule ip nat ' ~ chain ~ interface %} -{%   if protocol is not vyos_defined('all') %} +{% endif %} +{% set output = 'add rule ip nat ' ~ chain ~ interface %} +{% if protocol is not vyos_defined('all') %}  {%     set output = output ~ ' ip protocol ' ~ protocol %} -{%   endif %} -{%   if src_addr is vyos_defined %} +{% endif %} +{% if src_addr is vyos_defined %}  {%     set output = output ~ ' ' ~ src_addr %} -{%   endif %} -{%   if src_port is vyos_defined %} +{% endif %} +{% if src_port is vyos_defined %}  {%     set output = output ~ ' ' ~ protocol ~ ' ' ~ src_port %} -{%   endif %} -{%   if dst_addr is vyos_defined %} +{% endif %} +{% if dst_addr is vyos_defined %}  {%     set output = output ~ ' ' ~ dst_addr %} -{%   endif %} -{%   if dst_port is vyos_defined %} +{% endif %} +{% if dst_port is vyos_defined %}  {%     set output = output ~ ' ' ~ protocol ~ ' ' ~ dst_port %} -{%   endif %} -{#   Count packets #} -{%   set output = output ~ ' counter' %} -{#   Special handling of log option, we must repeat the entire rule before the #} -{#   NAT translation options are added, this is essential                      #} -{%   if log is vyos_defined %} +{% endif %} +{# Count packets #} +{% set output = output ~ ' counter' %} +{# Special handling of log option, we must repeat the entire rule before the #} +{# NAT translation options are added, this is essential                      #} +{% if log is vyos_defined %}  {%     set log_output = output ~ ' log prefix "' ~ log ~ '" comment "' ~ comment ~ '"' %} -{%   endif %} -{%   if trns_addr is vyos_defined %} +{% endif %} +{% if trns_addr is vyos_defined %}  {%     set output = output ~ ' ' ~ trns_addr %} -{%   endif %} -{%   if trns_port is vyos_defined %} +{% endif %} +{% if trns_port is vyos_defined %}  {#     Do not add a whitespace here, translation port must be directly added after IP address #}  {#     e.g. 192.0.2.10:3389                                                                   #}  {%     set output = output ~ trns_port %} -{%   endif %} -{%   if trns_opts is vyos_defined %} +{% endif %} +{% if trns_opts is vyos_defined %}  {%     set output = output ~ ' ' ~ trns_opts %} -{%   endif %} -{%   if comment is vyos_defined %} +{% endif %} +{% if comment is vyos_defined %}  {%     set output = output ~ ' comment "' ~ comment ~ '"' %} -{%   endif %} -{{ log_output if log_output is vyos_defined}} +{% endif %} +{{ log_output if log_output is vyos_defined }}  {{ output }} -{#   Special handling if protocol is tcp_udp, we must repeat the entire rule with udp as protocol #} -{%   if config.protocol is vyos_defined('tcp_udp') %} +{# Special handling if protocol is tcp_udp, we must repeat the entire rule with udp as protocol #} +{% if config.protocol is vyos_defined('tcp_udp') %}  {#     Beware of trailing whitespace, without it the comment tcp_udp will be changed to udp_udp #}  {{ log_output | replace('tcp ', 'udp ') if log_output is vyos_defined }}  {{ output | replace('tcp ', 'udp ') }} -{%   endif %} +{% endif %}  {% endmacro %}  # Start with clean SNAT and DNAT chains @@ -143,7 +143,7 @@ flush chain ip nat PREROUTING  flush chain ip nat POSTROUTING  {% if helper_functions is vyos_defined('remove') %}  {# NAT if going to be disabled - remove rules and targets from nftables #} -{%   set base_command = 'delete rule ip raw' %} +{%     set base_command = 'delete rule ip raw' %}  {{ base_command }} PREROUTING handle {{ pre_ct_ignore }}  {{ base_command }} OUTPUT     handle {{ out_ct_ignore }}  {{ base_command }} PREROUTING handle {{ pre_ct_conntrack }} @@ -155,7 +155,7 @@ delete chain ip raw NAT_CONNTRACK  {# NAT if enabled - add targets to nftables #}  add chain ip raw NAT_CONNTRACK  add rule ip raw NAT_CONNTRACK counter accept -{%   set base_command = 'add rule ip raw' %} +{%     set base_command = 'add rule ip raw' %}  {{ base_command }} PREROUTING position {{ pre_ct_ignore }}    counter jump VYOS_CT_HELPER  {{ base_command }} OUTPUT     position {{ out_ct_ignore }}    counter jump VYOS_CT_HELPER  {{ base_command }} PREROUTING position {{ pre_ct_conntrack }} counter jump NAT_CONNTRACK @@ -167,16 +167,16 @@ add rule ip raw NAT_CONNTRACK counter accept  #  add rule ip nat PREROUTING counter jump VYOS_PRE_DNAT_HOOK  {% if destination.rule is vyos_defined %} -{%   for rule, config in destination.rule.items() if config.disable is not vyos_defined %} +{%     for rule, config in destination.rule.items() if config.disable is not vyos_defined %}  {{ nat_rule(rule, config, 'PREROUTING') }} -{%   endfor %} +{%     endfor %}  {% endif %}  #  # Source NAT rules build up here  #  add rule ip nat POSTROUTING counter jump VYOS_PRE_SNAT_HOOK  {% if source.rule is vyos_defined %} -{%   for rule, config in source.rule.items() if config.disable is not vyos_defined %} +{%     for rule, config in source.rule.items() if config.disable is not vyos_defined %}  {{ nat_rule(rule, config, 'POSTROUTING') }} -{%   endfor %} +{%     endfor %}  {% endif %} diff --git a/data/templates/firewall/nftables-nat66.tmpl b/data/templates/firewall/nftables-nat66.j2 index ed98b888a..003b138b2 100644 --- a/data/templates/firewall/nftables-nat66.tmpl +++ b/data/templates/firewall/nftables-nat66.j2 @@ -1,22 +1,22 @@  #!/usr/sbin/nft -f  {% macro nptv6_rule(rule,config, chain) %} -{%   set comment  = '' %} -{%   set base_log = '' %} -{%   set src_prefix  = 'ip6 saddr ' ~ config.source.prefix if config.source.prefix is vyos_defined %} -{%   set dest_address  = 'ip6 daddr ' ~ config.destination.address if config.destination.address is vyos_defined %} -{%   if chain is vyos_defined('PREROUTING') %} +{% set comment  = '' %} +{% set base_log = '' %} +{% set src_prefix  = 'ip6 saddr ' ~ config.source.prefix if config.source.prefix is vyos_defined %} +{% set dest_address  = 'ip6 daddr ' ~ config.destination.address if config.destination.address is vyos_defined %} +{% if chain is vyos_defined('PREROUTING') %}  {%     set comment   = 'DST-NAT66-' ~ rule %}  {%     set base_log  = '[NAT66-DST-' ~ rule %}  {%     set interface = ' iifname "' ~ config.inbound_interface ~ '"' if config.inbound_interface is vyos_defined and config.inbound_interface is not vyos_defined('any') else '' %}  {%     if config.translation.address | is_ip_network %} -{#       support 1:1 network translation #} -{%       set dnat_type = 'dnat prefix to ' %} +{#         support 1:1 network translation #} +{%         set dnat_type = 'dnat prefix to ' %}  {%     else   %} -{%       set dnat_type = 'dnat to ' %} +{%         set dnat_type = 'dnat to ' %}  {%     endif %}  {%     set trns_address = dnat_type ~ config.translation.address if config.translation.address is vyos_defined %} -{%   elif chain is vyos_defined('POSTROUTING') %} +{% elif chain is vyos_defined('POSTROUTING') %}  {%     set comment   = 'SRC-NAT66-' ~ rule %}  {%     set base_log  = '[NAT66-SRC-' ~ rule %}  {%     if config.translation.address is vyos_defined %} @@ -33,34 +33,34 @@  {%         endif %}  {%     endif   %}  {%     set interface = ' oifname "' ~ config.outbound_interface ~ '"' if config.outbound_interface is vyos_defined else '' %} -{%   endif %} -{%   if config.log is vyos_defined %} +{% endif %} +{% if config.log is vyos_defined %}  {%     if config.translation.address is vyos_defined('masquerade') %} -{%       set log = base_log +'-MASQ]' %} +{%         set log = base_log ~ '-MASQ]' %}  {%     else %} -{%       set log = base_log ~ ']' %} +{%         set log = base_log ~ ']' %}  {%     endif %} -{%   endif %} -{%   set output = 'add rule ip6 nat ' ~ chain ~ interface %} -{#   Count packets #} -{%   set output = output ~ ' counter' %} -{#   Special handling of log option, we must repeat the entire rule before the #} -{#   NAT translation options are added, this is essential                      #} -{%   if log is vyos_defined %} +{% endif %} +{% set output = 'add rule ip6 nat ' ~ chain ~ interface %} +{# Count packets #} +{% set output = output ~ ' counter' %} +{# Special handling of log option, we must repeat the entire rule before the #} +{# NAT translation options are added, this is essential                      #} +{% if log is vyos_defined %}  {%     set log_output = output ~ ' log prefix "' ~ log ~ '" comment "' ~ comment ~ '"' %} -{%   endif %} -{%   if src_prefix is vyos_defined %} +{% endif %} +{% if src_prefix is vyos_defined %}  {%     set output = output ~ ' ' ~ src_prefix %} -{%   endif %} -{%   if dest_address is vyos_defined %} +{% endif %} +{% if dest_address is vyos_defined %}  {%     set output = output ~ ' ' ~ dest_address %} -{%   endif %} -{%   if trns_address is vyos_defined %} +{% endif %} +{% if trns_address is vyos_defined %}  {%     set output = output ~ ' ' ~ trns_address %} -{%   endif %} -{%   if comment is vyos_defined %} +{% endif %} +{% if comment is vyos_defined %}  {%     set output = output ~ ' comment "' ~ comment ~ '"' %} -{%   endif %} +{% endif %}  {{ log_output if log_output is vyos_defined }}  {{ output }}  {% endmacro %} @@ -69,9 +69,9 @@  flush table ip6 nat  {% if helper_functions is vyos_defined('remove') %}  {# NAT if going to be disabled - remove rules and targets from nftables #} -{%   set base_command = 'delete rule ip6 raw' %} -{{base_command}} PREROUTING handle {{ pre_ct_conntrack }} -{{base_command}} OUTPUT handle {{ out_ct_conntrack }} +{%     set base_command = 'delete rule ip6 raw' %} +{{ base_command }} PREROUTING handle {{ pre_ct_conntrack }} +{{ base_command }} OUTPUT handle {{ out_ct_conntrack }}  delete chain ip6 raw NAT_CONNTRACK @@ -79,7 +79,7 @@ delete chain ip6 raw NAT_CONNTRACK  {# NAT if enabled - add targets to nftables #}  add chain ip6 raw NAT_CONNTRACK  add rule ip6 raw NAT_CONNTRACK counter accept -{%   set base_command = 'add rule ip6 raw' %} +{%     set base_command = 'add rule ip6 raw' %}  {{ base_command }} PREROUTING position {{ pre_ct_conntrack }} counter jump NAT_CONNTRACK  {{ base_command }} OUTPUT     position {{ out_ct_conntrack }} counter jump NAT_CONNTRACK  {% endif %} @@ -88,15 +88,15 @@ add rule ip6 raw NAT_CONNTRACK counter accept  # Destination NAT66 rules build up here  #  {% if destination.rule is vyos_defined %} -{%   for rule, config in destination.rule.items() if config.disable is not vyos_defined %} +{%     for rule, config in destination.rule.items() if config.disable is not vyos_defined %}  {{ nptv6_rule(rule, config, 'PREROUTING') }} -{%   endfor %} +{%     endfor %}  {% endif %}  #  # Source NAT66 rules build up here  #  {% if source.rule is vyos_defined %} -{%   for rule, config in source.rule.items() if config.disable is not vyos_defined %} +{%     for rule, config in source.rule.items() if config.disable is not vyos_defined %}  {{ nptv6_rule(rule, config, 'POSTROUTING') }} -{%   endfor %} +{%     endfor %}  {% endif %} diff --git a/data/templates/firewall/nftables-policy.tmpl b/data/templates/firewall/nftables-policy.j2 index d1b0fa56e..0154c9f7e 100644 --- a/data/templates/firewall/nftables-policy.tmpl +++ b/data/templates/firewall/nftables-policy.j2 @@ -1,9 +1,9 @@  #!/usr/sbin/nft -f  {% if cleanup_commands is vyos_defined %} -{%   for command in cleanup_commands %} +{%     for command in cleanup_commands %}  {{ command }} -{%   endfor %} +{%     endfor %}  {% endif %}  include "/run/nftables_defines.conf" @@ -18,17 +18,17 @@ table ip mangle {      }  {% endif %}  {% if route is vyos_defined %} -{%   for route_text, conf in route.items() %} +{%     for route_text, conf in route.items() %}      chain VYOS_PBR_{{ route_text }} { -{%     if conf.rule is vyos_defined %} -{%       for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %} +{%         if conf.rule is vyos_defined %} +{%             for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}          {{ rule_conf | nft_rule(route_text, rule_id, 'ip') }} -{%       endfor %} -{%     endif %} +{%             endfor %} +{%         endif %}          {{ conf | nft_default_rule(route_text) }}      } -{%   endfor %} -{%- endif %} +{%     endfor %} +{% endif %}  }  table ip6 mangle { @@ -41,15 +41,15 @@ table ip6 mangle {      }  {% endif %}  {% if route6 is vyos_defined %} -{%   for route_text, conf in route6.items() %} +{%     for route_text, conf in route6.items() %}      chain VYOS_PBR6_{{ route_text }} { -{%     if conf.rule is vyos_defined %} -{%       for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %} +{%         if conf.rule is vyos_defined %} +{%             for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}          {{ rule_conf | nft_rule(route_text, rule_id, 'ip6') }} -{%       endfor %} -{%     endif %} +{%             endfor %} +{%         endif %}          {{ conf | nft_default_rule(route_text) }}      } -{%   endfor %} +{%     endfor %}  {% endif %}  } diff --git a/data/templates/firewall/nftables-vrf-zones.tmpl b/data/templates/firewall/nftables-vrf-zones.j2 index eecf47b78..eecf47b78 100644 --- a/data/templates/firewall/nftables-vrf-zones.tmpl +++ b/data/templates/firewall/nftables-vrf-zones.j2 diff --git a/data/templates/firewall/nftables.tmpl b/data/templates/firewall/nftables.j2 index 3a3f2e04c..fac3fad03 100644 --- a/data/templates/firewall/nftables.tmpl +++ b/data/templates/firewall/nftables.j2 @@ -1,9 +1,9 @@  #!/usr/sbin/nft -f  {% if cleanup_commands is vyos_defined %} -{%   for command in cleanup_commands %} +{%     for command in cleanup_commands %}  {{ command }} -{%   endfor %} +{%     endfor %}  {% endif %}  include "/run/nftables_defines.conf" @@ -31,39 +31,39 @@ table ip filter {      }  {% endif %}  {% if name is vyos_defined %} -{%   set ns = namespace(sets=[]) %} -{%   for name_text, conf in name.items() %} +{%     set ns = namespace(sets=[]) %} +{%     for name_text, conf in name.items() %}      chain NAME_{{ name_text }} { -{%     if conf.rule is vyos_defined %} -{%       for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %} +{%         if conf.rule is vyos_defined %} +{%             for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}          {{ rule_conf | nft_rule(name_text, rule_id) }} -{%         if rule_conf.recent is vyos_defined %} -{%           set ns.sets = ns.sets + [name_text + '_' + rule_id] %} +{%                 if rule_conf.recent is vyos_defined %} +{%                     set ns.sets = ns.sets + [name_text + '_' + rule_id] %} +{%                 endif %} +{%             endfor %}  {%         endif %} -{%       endfor %} -{%     endif %}          {{ conf | nft_default_rule(name_text) }}      } -{%   endfor %} -{%   for set_name in ns.sets %} +{%     endfor %} +{%     for set_name in ns.sets %}      set RECENT_{{ set_name }} {          type ipv4_addr          size 65535          flags dynamic      } -{%   endfor %} +{%     endfor %}  {% endif %}  {% if state_policy is vyos_defined %}      chain VYOS_STATE_POLICY { -{%   if state_policy.established is vyos_defined %} +{%     if state_policy.established is vyos_defined %}          {{ state_policy.established | nft_state_policy('established') }} -{%   endif %} -{%   if state_policy.invalid is vyos_defined %} +{%     endif %} +{%     if state_policy.invalid is vyos_defined %}          {{ state_policy.invalid | nft_state_policy('invalid') }} -{%   endif %} -{%   if state_policy.related is vyos_defined %} +{%     endif %} +{%     if state_policy.related is vyos_defined %}          {{ state_policy.related | nft_state_policy('related') }} -{%   endif %} +{%     endif %}          return      }  {% endif %} @@ -92,39 +92,39 @@ table ip6 filter {      }  {% endif %}  {% if ipv6_name is vyos_defined %} -{%   set ns = namespace(sets=[]) %} -{%   for name_text, conf in ipv6_name.items() %} +{%     set ns = namespace(sets=[]) %} +{%     for name_text, conf in ipv6_name.items() %}      chain NAME6_{{ name_text }} { -{%     if conf.rule is vyos_defined %} -{%       for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %} +{%         if conf.rule is vyos_defined %} +{%             for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}          {{ rule_conf | nft_rule(name_text, rule_id, 'ip6') }} -{%         if rule_conf.recent is vyos_defined %} -{%           set ns.sets = ns.sets + [name_text + '_' + rule_id] %} +{%                 if rule_conf.recent is vyos_defined %} +{%                     set ns.sets = ns.sets + [name_text + '_' + rule_id] %} +{%                 endif %} +{%             endfor %}  {%         endif %} -{%       endfor %} -{%     endif %}          {{ conf | nft_default_rule(name_text) }}      } -{%   endfor %} -{%   for set_name in ns.sets %} +{%     endfor %} +{%     for set_name in ns.sets %}      set RECENT6_{{ set_name }} {          type ipv6_addr          size 65535          flags dynamic      } -{%   endfor %} +{%     endfor %}  {% endif %}  {% if state_policy is vyos_defined %}      chain VYOS_STATE_POLICY6 { -{%   if state_policy.established is vyos_defined %} +{%     if state_policy.established is vyos_defined %}          {{ state_policy.established | nft_state_policy('established', ipv6=True) }} -{%   endif %} -{%   if state_policy.invalid is vyos_defined %} +{%     endif %} +{%     if state_policy.invalid is vyos_defined %}          {{ state_policy.invalid | nft_state_policy('invalid', ipv6=True) }} -{%   endif %} -{%   if state_policy.related is vyos_defined %} +{%     endif %} +{%     if state_policy.related is vyos_defined %}          {{ state_policy.related | nft_state_policy('related', ipv6=True) }} -{%   endif %} +{%     endif %}          return      }  {% endif %} diff --git a/data/templates/firewall/upnpd.conf.tmpl b/data/templates/firewall/upnpd.conf.j2 index 6e73995fa..27573cbf9 100644 --- a/data/templates/firewall/upnpd.conf.tmpl +++ b/data/templates/firewall/upnpd.conf.j2 @@ -5,9 +5,9 @@ ext_ifname={{ wan_interface }}  {% if wan_ip is vyos_defined %}  # If the WAN interface has several IP addresses, you  # can specify the one to use below -{%   for addr in wan_ip %} +{%     for addr in wan_ip %}  ext_ip={{ addr }} -{%   endfor  %} +{%     endfor  %}  {% endif %}  # LAN network interfaces IPs / networks @@ -20,15 +20,15 @@ ext_ip={{ addr }}  # When MULTIPLE_EXTERNAL_IP is enabled, the external IP  # address associated with the subnet follows. For example:  #  listening_ip=192.168.0.1/24 88.22.44.13 -{%   for addr in listen %} -{%     if addr | is_ipv4  %} +{%     for addr in listen %} +{%         if addr | is_ipv4  %}  listening_ip={{ addr }} -{%     elif addr | is_ipv6  %} +{%         elif addr | is_ipv6  %}  ipv6_listening_ip={{ addr }} -{%     else %} +{%         else %}  listening_ip={{ addr }} -{%     endif  %} -{%   endfor  %} +{%         endif  %} +{%     endfor  %}  {% endif %}  # CAUTION: mixing up WAN and LAN interfaces may introduce security risks! @@ -57,12 +57,12 @@ enable_upnp=yes  # PCP  # Configure the minimum and maximum lifetime of a port mapping in seconds  # 120s and 86400s (24h) are suggested values from PCP-base -{% if pcp_lifetime.max is vyos_defined %} +{%     if pcp_lifetime.max is vyos_defined %}  max_lifetime={{ pcp_lifetime.max }} -{% endif %} -{% if pcp_lifetime.min is vyos_defined %} +{%     endif %} +{%     if pcp_lifetime.min is vyos_defined %}  min_lifetime={{ pcp_lifetime.min }} -{% endif %} +{%     endif %}  {% endif %} @@ -142,11 +142,11 @@ lease_file=/config/upnp.leases  # modify the IP ranges to match their own internal networks, and  # also consider implementing network-specific restrictions  # CAUTION: failure to enforce any rules may permit insecure requests to be made! -{% for rule, config in rules.items() %} -{%  if config.disable is vyos_defined %} -{{ config.action}} {{ config.external_port_range }} {{ config.ip }} {{ config.internal_port_range }} -{%  endif %} -{% endfor %} +{%     for rule, config in rules.items() %} +{%         if config.disable is vyos_defined %} +{{ config.action }} {{ config.external_port_range }} {{ config.ip }} {{ config.internal_port_range }} +{%         endif %} +{%     endfor %}  {% endif %}  {% if stun is vyos_defined %} diff --git a/data/templates/frr/daemons.frr.tmpl b/data/templates/frr/daemons.frr.tmpl new file mode 100644 index 000000000..ab7b14d6b --- /dev/null +++ b/data/templates/frr/daemons.frr.tmpl @@ -0,0 +1,54 @@ +zebra=yes +bgpd=yes +ospfd=yes +ospf6d=yes +ripd=yes +ripngd=yes +isisd=yes +pimd=no +ldpd=yes +nhrpd=no +eigrpd=no +babeld=no +sharpd=no +pbrd=no +bfdd=yes +staticd=yes + +vtysh_enable=yes +zebra_options="  -s 90000000 --daemon -A 127.0.0.1 +{%- if irdp is defined %} -M irdp{% endif -%} +{%- if snmp is defined and snmp.zebra is defined %} -M snmp{% endif -%} +" +bgpd_options="   --daemon -A 127.0.0.1 +{%- if bmp is defined %} -M bmp{% endif -%} +{%- if snmp is defined and snmp.bgpd is defined %} -M snmp{% endif -%} +" +ospfd_options="  --daemon -A 127.0.0.1 +{%- if snmp is defined and snmp.ospfd is defined %} -M snmp{% endif -%} +" +ospf6d_options=" --daemon -A ::1 +{%- if snmp is defined and snmp.ospf6d is defined %} -M snmp{% endif -%} +" +ripd_options="   --daemon -A 127.0.0.1 +{%- if snmp is defined and snmp.ripd is defined %} -M snmp{% endif -%} +" +ripngd_options=" --daemon -A ::1" +isisd_options="  --daemon -A 127.0.0.1 +{%- if snmp is defined and snmp.isisd is defined %} -M snmp{% endif -%} +" +pimd_options="  --daemon -A 127.0.0.1" +ldpd_options="  --daemon -A 127.0.0.1 +{%- if snmp is defined and snmp.ldpd is defined %} -M snmp{% endif -%} +" +nhrpd_options="  --daemon -A 127.0.0.1" +eigrpd_options="  --daemon -A 127.0.0.1" +babeld_options="  --daemon -A 127.0.0.1" +sharpd_options="  --daemon -A 127.0.0.1" +pbrd_options="  --daemon -A 127.0.0.1" +staticd_options="  --daemon -A 127.0.0.1" +bfdd_options="  --daemon -A 127.0.0.1" + +watchfrr_enable=no +valgrind_enable=no + diff --git a/data/templates/frr/policy.frr.j2 b/data/templates/frr/policy.frr.j2 index 9f3097f82..a42b73e98 100644 --- a/data/templates/frr/policy.frr.j2 +++ b/data/templates/frr/policy.frr.j2 @@ -238,11 +238,14 @@ route-map {{ route_map }} {{ rule_config.action }} {{ rule }}  {%                     if rule_config.set.aggregator.as is vyos_defined and rule_config.set.aggregator.ip is vyos_defined %}   set aggregator as {{ rule_config.set.aggregator.as }} {{ rule_config.set.aggregator.ip }}  {%                     endif %} -{%                     if rule_config.set.as_path_exclude is vyos_defined %} - set as-path exclude {{ rule_config.set.as_path_exclude }} +{%                     if rule_config.set.as_path.exclude is vyos_defined %} + set as-path exclude {{ rule_config.set.as_path.exclude }}  {%                     endif %} -{%                     if rule_config.set.as_path_prepend is vyos_defined %} - set as-path prepend {{ rule_config.set.as_path_prepend }} +{%                     if rule_config.set.as_path.prepend is vyos_defined %} + set as-path prepend {{ rule_config.set.as_path.prepend }} +{%                     endif %} +{%                     if rule_config.set.as_path.prepend_last_as is vyos_defined %} + set as-path prepend last-as {{ rule_config.set.as_path.prepend_last_as }}  {%                     endif %}  {%                     if rule_config.set.atomic_aggregate is vyos_defined %}   set atomic-aggregate @@ -256,6 +259,12 @@ route-map {{ route_map }} {{ rule_config.action }} {{ rule }}  {%                     if rule_config.set.distance is vyos_defined %}   set distance {{ rule_config.set.distance }}  {%                     endif %} +{%                     if rule_config.set.evpn.gateway.ipv4 is vyos_defined %} + set evpn gateway-ip ipv4 {{ rule_config.set.evpn.gateway.ipv4 }} +{%                     endif %} +{%                     if rule_config.set.evpn.gateway.ipv6 is vyos_defined %} + set evpn gateway-ip ipv6 {{ rule_config.set.evpn.gateway.ipv6 }} +{%                     endif %}  {%                     if rule_config.set.extcommunity.bandwidth is vyos_defined %}   set extcommunity bandwidth {{ rule_config.set.extcommunity.bandwidth }}  {%                     endif %} diff --git a/data/templates/getty/serial-getty.service.tmpl b/data/templates/getty/serial-getty.service.j2 index 0183eae7d..0183eae7d 100644 --- a/data/templates/getty/serial-getty.service.tmpl +++ b/data/templates/getty/serial-getty.service.j2 diff --git a/data/templates/high-availability/keepalived.conf.j2 b/data/templates/high-availability/keepalived.conf.j2 new file mode 100644 index 000000000..6684dbc2c --- /dev/null +++ b/data/templates/high-availability/keepalived.conf.j2 @@ -0,0 +1,169 @@ +# Autogenerated by VyOS +# Do not edit this file, all your changes will be lost +# on next commit or reboot + +global_defs { +    dynamic_interfaces +    script_user root +    notify_fifo /run/keepalived/keepalived_notify_fifo +    notify_fifo_script /usr/libexec/vyos/system/keepalived-fifo.py +} + +{% if vrrp.group is vyos_defined %} +{%     for name, group_config in vrrp.group.items() if group_config.disable is not vyos_defined %} +{%         if group_config.health_check.script is vyos_defined %} +vrrp_script healthcheck_{{ name }} { +    script "{{ group_config.health_check.script }}" +    interval {{ group_config.health_check.interval }} +    fall {{ group_config.health_check.failure_count }} +    rise 1 +} +{%         endif %} +vrrp_instance {{ name }} { +{%         if group_config.description is vyos_defined %} +    # {{ group_config.description }} +{%         endif %} +    state BACKUP +    interface {{ group_config.interface }} +    virtual_router_id {{ group_config.vrid }} +    priority {{ group_config.priority }} +    advert_int {{ group_config.advertise_interval }} +{%         if group_config.track.exclude_vrrp_interface is vyos_defined %} +    dont_track_primary +{%         endif %} +{%         if group_config.no_preempt is not vyos_defined and group_config.preempt_delay is vyos_defined %} +    preempt_delay {{ group_config.preempt_delay }} +{%         elif group_config.no_preempt is vyos_defined %} +    nopreempt +{%         endif %} +{%         if group_config.peer_address is vyos_defined %} +    unicast_peer { {{ group_config.peer_address }} } +{%         endif %} +{%         if group_config.hello_source_address is vyos_defined %} +{%             if group_config.peer_address is vyos_defined %} +    unicast_src_ip {{ group_config.hello_source_address }} +{%             else %} +    mcast_src_ip {{ group_config.hello_source_address }} +{%             endif %} +{%         endif %} +{%         if group_config.rfc3768_compatibility is vyos_defined and group_config.peer_address is vyos_defined %} +    use_vmac {{ group_config.interface }}v{{ group_config.vrid }} +    vmac_xmit_base +{%         elif group_config.rfc3768_compatibility is vyos_defined %} +    use_vmac {{ group_config.interface }}v{{ group_config.vrid }} +{%         endif %} +{%         if group_config.authentication is vyos_defined %} +    authentication { +        auth_pass "{{ group_config.authentication.password }}" +{%             if group_config.authentication.type is vyos_defined('plaintext-password') %} +        auth_type PASS +{%             else %} +        auth_type {{ group_config.authentication.type | upper }} +{%             endif %} +    } +{%         endif %} +{%         if group_config.address is vyos_defined %} +    virtual_ipaddress { +{%             for addr, addr_config in group_config.address.items() %} +        {{ addr }}{{ ' dev ' + addr_config.interface if addr_config.interface is vyos_defined }} +{%             endfor %} +    } +{%         endif %} +{%         if group_config.excluded_address is vyos_defined %} +    virtual_ipaddress_excluded { +{%             for addr in group_config.excluded_address %} +        {{ addr }} +{%             endfor %} +    } +{%         endif %} +{%         if group_config.track.interface is vyos_defined %} +    track_interface { +{%             for interface in group_config.track.interface %} +        {{ interface }} +{%             endfor %} +    } +{%         endif %} +{%         if group_config.health_check.script is vyos_defined %} +    track_script { +        healthcheck_{{ name }} +    } +{%         endif %} +} +{%     endfor %} +{% endif %} + +{% if vrrp.sync_group is vyos_defined %} +{%     for name, sync_group_config in vrrp.sync_group.items() if sync_group_config.disable is not vyos_defined %} +vrrp_sync_group {{ name }} { +    group { +{%         if sync_group_config.member is vyos_defined %} +{%             for member in sync_group_config.member %} +        {{ member }} +{%             endfor %} +{%         endif %} +    } + +{# Health-check scripts should be in section sync-group if member is part of the sync-group T4081 #} +{%         if vrrp.group is vyos_defined %} +{%             for name, group_config in vrrp.group.items() if group_config.disable is not vyos_defined %} +{%                 if group_config.health_check.script is vyos_defined and name in sync_group_config.member %} +    track_script { +        healthcheck_{{ name }} +    } +{%                 endif %} +{%             endfor %} +{%         endif %} +{%         if conntrack_sync_group is vyos_defined(name) %} +{%             set vyos_helper = "/usr/libexec/vyos/vyos-vrrp-conntracksync.sh" %} +    notify_master "{{ vyos_helper }} master {{ name }}" +    notify_backup "{{ vyos_helper }} backup {{ name }}" +    notify_fault "{{ vyos_helper }} fault {{ name }}" +{%         endif %} +} +{%     endfor %} +{% endif %} + +{% if virtual_server is vyos_defined %} +# Virtual-server configuration +{%     for vserver, vserver_config in virtual_server.items() %} +virtual_server {{ vserver }} {{ vserver_config.port }} { +    delay_loop {{ vserver_config.delay_loop }} +{%         if vserver_config.algorithm is vyos_defined('round-robin') %} +    lb_algo rr +{%         elif vserver_config.algorithm is vyos_defined('weighted-round-robin') %} +    lb_algo wrr +{%         elif vserver_config.algorithm is vyos_defined('least-connection') %} +    lb_algo lc +{%         elif vserver_config.algorithm is vyos_defined('weighted-least-connection') %} +    lb_algo wlc +{%         elif vserver_config.algorithm is vyos_defined('source-hashing') %} +    lb_algo sh +{%         elif vserver_config.algorithm is vyos_defined('destination-hashing') %} +    lb_algo dh +{%         elif vserver_config.algorithm is vyos_defined('locality-based-least-connection') %} +    lb_algo lblc +{%         endif %} +{%         if vserver_config.forward_method is vyos_defined('nat') %} +    lb_kind NAT +{%         elif vserver_config.forward_method is vyos_defined('direct') %} +    lb_kind DR +{%         elif vserver_config.forward_method is vyos_defined('tunnel') %} +    lb_kind TUN +{%         endif %} +    persistence_timeout {{ vserver_config.persistence_timeout }} +    protocol {{ vserver_config.protocol | upper }} +{%         if vserver_config.real_server is vyos_defined %} +{%             for rserver, rserver_config in vserver_config.real_server.items() %} +    real_server {{ rserver }} {{ rserver_config.port }} { +        weight 1 +        {{ vserver_config.protocol | upper }}_CHECK { +{%                 if rserver_config.connection_timeout is vyos_defined %} +            connect_timeout {{ rserver_config.connection_timeout }} +{%                 endif %} +        } +    } +{%             endfor %} +{%         endif %} +} +{%     endfor %} +{% endif %} diff --git a/data/templates/high-availability/keepalived.conf.tmpl b/data/templates/high-availability/keepalived.conf.tmpl deleted file mode 100644 index 202760251..000000000 --- a/data/templates/high-availability/keepalived.conf.tmpl +++ /dev/null @@ -1,169 +0,0 @@ -# Autogenerated by VyOS -# Do not edit this file, all your changes will be lost -# on next commit or reboot - -global_defs { -    dynamic_interfaces -    script_user root -    notify_fifo /run/keepalived/keepalived_notify_fifo -    notify_fifo_script /usr/libexec/vyos/system/keepalived-fifo.py -} - -{% if vrrp.group is vyos_defined %} -{%   for name, group_config in vrrp.group.items() if group_config.disable is not vyos_defined %} -{%     if group_config.health_check.script is vyos_defined %} -vrrp_script healthcheck_{{ name }} { -    script "{{ group_config.health_check.script }}" -    interval {{ group_config.health_check.interval }} -    fall {{ group_config.health_check.failure_count }} -    rise 1 -} -{%     endif %} -vrrp_instance {{ name }} { -{%     if group_config.description is vyos_defined %} -    # {{ group_config.description }} -{%     endif %} -    state BACKUP -    interface {{ group_config.interface }} -    virtual_router_id {{ group_config.vrid }} -    priority {{ group_config.priority }} -    advert_int {{ group_config.advertise_interval }} -{%     if group_config.track.exclude_vrrp_interface is vyos_defined %} -    dont_track_primary -{%     endif %} -{%     if group_config.no_preempt is not vyos_defined and group_config.preempt_delay is vyos_defined %} -    preempt_delay {{ group_config.preempt_delay }} -{%     elif group_config.no_preempt is vyos_defined %} -    nopreempt -{%     endif %} -{%     if group_config.peer_address is vyos_defined %} -    unicast_peer { {{ group_config.peer_address }} } -{%     endif %} -{%     if group_config.hello_source_address is vyos_defined %} -{%       if group_config.peer_address is vyos_defined %} -    unicast_src_ip {{ group_config.hello_source_address }} -{%       else %} -    mcast_src_ip {{ group_config.hello_source_address }} -{%       endif %} -{%     endif %} -{%     if group_config.rfc3768_compatibility is vyos_defined and group_config.peer_address is vyos_defined %} -    use_vmac {{ group_config.interface }}v{{ group_config.vrid }} -    vmac_xmit_base -{%     elif group_config.rfc3768_compatibility is vyos_defined %} -    use_vmac {{ group_config.interface }}v{{ group_config.vrid }} -{%     endif %} -{%     if group_config.authentication is vyos_defined %} -    authentication { -        auth_pass "{{ group_config.authentication.password }}" -{%       if group_config.authentication.type is vyos_defined('plaintext-password') %} -        auth_type PASS -{%       else %} -        auth_type {{ group_config.authentication.type | upper }} -{%       endif %} -    } -{%     endif %} -{%     if group_config.address is vyos_defined %} -    virtual_ipaddress { -{%       for addr, addr_config in group_config.address.items() %} -        {{ addr }}{{ ' dev ' + addr_config.interface if addr_config.interface is vyos_defined }} -{%       endfor %} -    } -{%     endif %} -{%     if group_config.excluded_address is vyos_defined %} -    virtual_ipaddress_excluded { -{%       for addr in group_config.excluded_address %} -        {{ addr }} -{%       endfor %} -    } -{%     endif %} -{%     if group_config.track.interface is vyos_defined %} -    track_interface { -{%       for interface in group_config.track.interface %} -        {{ interface }} -{%       endfor %} -    } -{%     endif %} -{%     if group_config.health_check.script is vyos_defined %} -    track_script { -        healthcheck_{{ name }} -    } -{%     endif %} -} -{%   endfor %} -{% endif %} - -{% if vrrp.sync_group is vyos_defined %} -{%   for name, sync_group_config in vrrp.sync_group.items() if sync_group_config.disable is not vyos_defined %} -vrrp_sync_group {{ name }} { -    group { -{%     if sync_group_config.member is vyos_defined %} -{%       for member in sync_group_config.member %} -        {{ member }} -{%       endfor %} -{%     endif %} -    } - -{# Health-check scripts should be in section sync-group if member is part of the sync-group T4081 #} -{%     if vrrp.group is vyos_defined %} -{%       for name, group_config in vrrp.group.items() if group_config.disable is not vyos_defined %} -{%         if group_config.health_check.script is vyos_defined and name in sync_group_config.member %} -    track_script { -        healthcheck_{{ name }} -    } -{%         endif %} -{%       endfor %} -{%     endif %} -{%     if conntrack_sync_group is vyos_defined(name) %} -{%     set vyos_helper = "/usr/libexec/vyos/vyos-vrrp-conntracksync.sh" %} -    notify_master "{{ vyos_helper }} master {{ name }}" -    notify_backup "{{ vyos_helper }} backup {{ name }}" -    notify_fault "{{ vyos_helper }} fault {{ name }}" -{%     endif %} -} -{%   endfor %} -{% endif %} - -{% if virtual_server is vyos_defined %} -# Virtual-server configuration -{%   for vserver, vserver_config in virtual_server.items() %} -virtual_server {{ vserver }} {{ vserver_config.port }} { -    delay_loop {{ vserver_config.delay_loop }} -{%     if vserver_config.algorithm is vyos_defined('round-robin') %} -    lb_algo rr -{%     elif vserver_config.algorithm is vyos_defined('weighted-round-robin') %} -    lb_algo wrr -{%     elif vserver_config.algorithm is vyos_defined('least-connection') %} -    lb_algo lc -{%     elif vserver_config.algorithm is vyos_defined('weighted-least-connection') %} -    lb_algo wlc -{%     elif vserver_config.algorithm is vyos_defined('source-hashing') %} -    lb_algo sh -{%     elif vserver_config.algorithm is vyos_defined('destination-hashing') %} -    lb_algo dh -{%     elif vserver_config.algorithm is vyos_defined('locality-based-least-connection') %} -    lb_algo lblc -{%     endif %} -{%     if vserver_config.forward_method is vyos_defined('nat') %} -    lb_kind NAT -{%     elif vserver_config.forward_method is vyos_defined('direct') %} -    lb_kind DR -{%     elif vserver_config.forward_method is vyos_defined('tunnel') %} -    lb_kind TUN -{%     endif %} -    persistence_timeout {{ vserver_config.persistence_timeout }} -    protocol {{ vserver_config.protocol | upper }} -{%     if vserver_config.real_server is vyos_defined %} -{%       for rserver, rserver_config in vserver_config.real_server.items() %} -    real_server {{ rserver }} {{ rserver_config.port }} { -        weight 1 -        {{ vserver_config.protocol | upper }}_CHECK { -{%         if rserver_config.connection_timeout is vyos_defined %} -            connect_timeout {{ rserver_config.connection_timeout }} -{%         endif %} -        } -    } -{%        endfor %} -{%      endif %} -} -{%   endfor %} -{% endif %} diff --git a/data/templates/https/nginx.default.tmpl b/data/templates/https/nginx.default.j2 index a51505270..70e62ae7a 100644 --- a/data/templates/https/nginx.default.tmpl +++ b/data/templates/https/nginx.default.j2 @@ -1,59 +1,56 @@  ### Autogenerated by https.py ###  # Default server configuration -#  {% for server in server_block_list %}  server { -          # SSL configuration          # -{% if server.address == '*' %} +{%     if server.address == '*' %}          listen {{ server.port }} ssl;          listen [::]:{{ server.port }} ssl; -{% else %} +{%     else %}          listen {{ server.address | bracketize_ipv6 }}:{{ server.port }} ssl; -{% endif %} +{%     endif %} -{% for name in server.name %} +{%     for name in server.name %}          server_name {{ name }}; -{% endfor %} +{%     endfor %} -{% if server.certbot %} +{%     if server.certbot %}          ssl_certificate {{ server.certbot_dir }}/live/{{ server.certbot_domain_dir }}/fullchain.pem;          ssl_certificate_key {{ server.certbot_dir }}/live/{{ server.certbot_domain_dir }}/privkey.pem;          include {{ server.certbot_dir }}/options-ssl-nginx.conf;          ssl_dhparam {{ server.certbot_dir }}/ssl-dhparams.pem; -{% elif server.vyos_cert %} +{%     elif server.vyos_cert %}          ssl_certificate {{ server.vyos_cert.crt }};          ssl_certificate_key {{ server.vyos_cert.key }}; -{% else %} +{%     else %}          #          # Self signed certs generated by the ssl-cert package          # Don't use them in a production server!          #          include snippets/snakeoil.conf; -{% endif %} +{%     endif %}          ssl_protocols TLSv1.2 TLSv1.3;          # proxy settings for HTTP API, if enabled; 503, if not          location ~ /(retrieve|configure|config-file|image|generate|show|docs|openapi.json|redoc|graphql) { -{% if server.api %} -{% if server.api.socket %} +{%     if server.api %} +{%         if server.api.socket %}                  proxy_pass http://unix:/run/api.sock; -{% else %} +{%         else %}                  proxy_pass http://localhost:{{ server.api.port }}; -{% endif %} +{%         endif %}                  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;                  proxy_set_header X-Forwarded-Proto $scheme;                  proxy_read_timeout 600;                  proxy_buffering off; -{% else %} +{%     else %}                  return 503; -{% endif %} +{%     endif %}          }          error_page 497 =301 https://$host:{{ server.port }}$request_uri; -  }  {% endfor %} diff --git a/data/templates/https/override.conf.tmpl b/data/templates/https/override.conf.j2 index c2c191b06..c2c191b06 100644 --- a/data/templates/https/override.conf.tmpl +++ b/data/templates/https/override.conf.j2 diff --git a/data/templates/https/vyos-http-api.service.tmpl b/data/templates/https/vyos-http-api.service.j2 index fb424e06c..fb424e06c 100644 --- a/data/templates/https/vyos-http-api.service.tmpl +++ b/data/templates/https/vyos-http-api.service.j2 diff --git a/data/templates/ids/fastnetmon.tmpl b/data/templates/ids/fastnetmon.j2 index b6bef9a68..c482002fa 100644 --- a/data/templates/ids/fastnetmon.tmpl +++ b/data/templates/ids/fastnetmon.j2 @@ -29,22 +29,22 @@ enable_subnet_counters = off  mirror_afpacket = on  {% endif %} -process_incoming_traffic = {{ 'on' if direction is vyos_defined and 'in' in direction else 'off '}} -process_outgoing_traffic = {{ 'on' if direction is vyos_defined and 'out' in direction else 'off '}} +process_incoming_traffic = {{ 'on' if direction is vyos_defined and 'in' in direction else 'off' }} +process_outgoing_traffic = {{ 'on' if direction is vyos_defined and 'out' in direction else 'off' }}  {% if threshold is vyos_defined %} -{%   for thr, thr_value in threshold.items() %} -{%     if thr is vyos_defined('fps') %} +{%     for thr, thr_value in threshold.items() %} +{%         if thr is vyos_defined('fps') %}  ban_for_flows = on  threshold_flows = {{ thr_value }} -{%     elif thr is vyos_defined('mbps') %} +{%         elif thr is vyos_defined('mbps') %}  ban_for_bandwidth = on  threshold_mbps = {{ thr_value }} -{%     elif thr is vyos_defined('pps') %} +{%         elif thr is vyos_defined('pps') %}  ban_for_pps = on  threshold_pps = {{ thr_value }} -{%     endif %} -{%   endfor %} +{%         endif %} +{%     endfor %}  {% endif %}  {% if listen_interface is vyos_defined %} diff --git a/data/templates/ids/fastnetmon_networks_list.tmpl b/data/templates/ids/fastnetmon_networks_list.j2 index ab9add22c..1c81180be 100644 --- a/data/templates/ids/fastnetmon_networks_list.tmpl +++ b/data/templates/ids/fastnetmon_networks_list.j2 @@ -1,7 +1,7 @@  {% if network is vyos_defined(var_type=str) %}  {{ network }}  {% else %} -{%   for net in network %} +{%     for net in network %}  {{ net }} -{%   endfor %} +{%     endfor %}  {% endif %} diff --git a/data/templates/igmp-proxy/igmpproxy.conf.tmpl b/data/templates/igmp-proxy/igmpproxy.conf.j2 index f32d68e43..ab3c9fd31 100644 --- a/data/templates/igmp-proxy/igmpproxy.conf.tmpl +++ b/data/templates/igmp-proxy/igmpproxy.conf.j2 @@ -18,23 +18,23 @@  quickleave  {% endif %}  {% if interface is vyos_defined %} -{%   for iface, config in interface.items() %} +{%     for iface, config in interface.items() %}  # Configuration for {{ iface }} ({{ config.role }} interface) -{%     if config.role is vyos_defined('disabled') %} +{%         if config.role is vyos_defined('disabled') %}  phyint {{ iface }} disabled -{%     else %} +{%         else %}  phyint {{ iface }} {{ config.role }} ratelimit 0 threshold {{ config.threshold }} -{%     endif %} -{%     if config.alt_subnet is vyos_defined %} -{%       for subnet in config.alt_subnet %} +{%         endif %} +{%         if config.alt_subnet is vyos_defined %} +{%             for subnet in config.alt_subnet %}          altnet {{ subnet }} -{%       endfor %} -{%     endif %} -{%     if config.whitelist is vyos_defined %} -{%       for subnet in config.whitelist %} +{%             endfor %} +{%         endif %} +{%         if config.whitelist is vyos_defined %} +{%             for subnet in config.whitelist %}          whitelist {{ subnet }} -{%       endfor %} -{%     endif %} -{%   endfor %} +{%             endfor %} +{%         endif %} +{%     endfor %}  {% endif %} diff --git a/data/templates/ipsec/charon.tmpl b/data/templates/ipsec/charon.j2 index 2eac24eaa..388559af8 100644 --- a/data/templates/ipsec/charon.tmpl +++ b/data/templates/ipsec/charon.j2 @@ -1,6 +1,5 @@  # Options for the charon IKE daemon.  charon { -      # Accept unencrypted ID and HASH payloads in IKEv1 Main Mode.      # accept_unencrypted_mainmode_messages = no @@ -23,13 +22,13 @@ charon {      # Cisco FlexVPN  {% if options is vyos_defined %}      cisco_flexvpn = {{ 'yes' if options.flexvpn is vyos_defined else 'no' }} -{%   if options.virtual_ip is vyos_defined %} +{%     if options.virtual_ip is vyos_defined %}      install_virtual_ip = yes -{%   endif %} -{%   if options.interface is vyos_defined %} +{%     endif %} +{%     if options.interface is vyos_defined %}      install_virtual_ip_on = {{ options.interface }} -{%   endif %} -{%  endif %} +{%     endif %} +{% endif %}      # Close the IKE_SA if setup of the CHILD_SA along with IKE_AUTH failed.      # close_ike_on_child_failure = no diff --git a/data/templates/ipsec/charon/dhcp.conf.tmpl b/data/templates/ipsec/charon/dhcp.conf.j2 index aaa5613fb..aaa5613fb 100644 --- a/data/templates/ipsec/charon/dhcp.conf.tmpl +++ b/data/templates/ipsec/charon/dhcp.conf.j2 diff --git a/data/templates/ipsec/charon/eap-radius.conf.tmpl b/data/templates/ipsec/charon/eap-radius.conf.j2 index b58022521..8495011fe 100644 --- a/data/templates/ipsec/charon/eap-radius.conf.tmpl +++ b/data/templates/ipsec/charon/eap-radius.conf.j2 @@ -94,19 +94,19 @@ eap-radius {      # Section to specify multiple RADIUS servers.      servers { -{%  if remote_access.radius.server is vyos_defined %} -{%    for server, server_options in remote_access.radius.server.items() if server_options.disable is not vyos_defined %} +{% if remote_access.radius.server is vyos_defined %} +{%     for server, server_options in remote_access.radius.server.items() if server_options.disable is not vyos_defined %}          {{ server | replace('.', '-') }} {              address = {{ server }}              secret = {{ server_options.key }}              auth_port = {{ server_options.port }} -{%      if server_options.disable_accounting is not vyos_defined %} -            acct_port = {{ server_options.port | int +1 }} -{%  endif %} +{%         if server_options.disable_accounting is not vyos_defined %} +            acct_port = {{ server_options.port | int + 1 }} +{%         endif %}              sockets = 20          } -{%    endfor %} -{%  endif %} +{%     endfor %} +{% endif %}      }      # Section to configure multiple XAuth authentication rounds via RADIUS. diff --git a/data/templates/ipsec/interfaces_use.conf.tmpl b/data/templates/ipsec/interfaces_use.conf.j2 index 55c3ce4f3..c1bf8270d 100644 --- a/data/templates/ipsec/interfaces_use.conf.tmpl +++ b/data/templates/ipsec/interfaces_use.conf.j2 @@ -1,5 +1,5 @@ -{%  if interface is vyos_defined %} +{% if interface is vyos_defined %}  charon {      interfaces_use = {{ ', '.join(interface) }}  } -{%  endif %}
\ No newline at end of file +{% endif %}
\ No newline at end of file diff --git a/data/templates/ipsec/ios_profile.tmpl b/data/templates/ipsec/ios_profile.j2 index c8e17729a..c8e17729a 100644 --- a/data/templates/ipsec/ios_profile.tmpl +++ b/data/templates/ipsec/ios_profile.j2 diff --git a/data/templates/ipsec/ipsec.conf.j2 b/data/templates/ipsec/ipsec.conf.j2 new file mode 100644 index 000000000..f63995b38 --- /dev/null +++ b/data/templates/ipsec/ipsec.conf.j2 @@ -0,0 +1,19 @@ +# Created by VyOS - manual changes will be overwritten + +config setup +{% set charondebug = '' %} +{% if log.subsystem is vyos_defined %} +{%     set subsystem = log.subsystem %} +{%     if 'any' in log.subsystem %} +{%         set subsystem = ['dmn', 'mgr', 'ike', 'chd','job', 'cfg', 'knl', +                            'net', 'asn', 'enc', 'lib', 'esp', 'tls', 'tnc', +                            'imc', 'imv', 'pts'] %} +{%     endif %} +{%     set charondebug = subsystem | join (' ' ~ log.level ~ ', ') ~ ' ' ~ log.level %} +{% endif %} +    charondebug = "{{ charondebug }}" +    uniqueids = {{ "no" if disable_uniqreqids is vyos_defined else "yes" }} + +{% if include_ipsec_conf is vyos_defined %} +include {{ include_ipsec_conf }} +{% endif %} diff --git a/data/templates/ipsec/ipsec.conf.tmpl b/data/templates/ipsec/ipsec.conf.tmpl deleted file mode 100644 index 0f7131dff..000000000 --- a/data/templates/ipsec/ipsec.conf.tmpl +++ /dev/null @@ -1,18 +0,0 @@ -# Created by VyOS - manual changes will be overwritten - -config setup -{% set charondebug = '' %} -{% if log.subsystem is vyos_defined %} -{%   set subsystem = log.subsystem %} -{%   if 'any' in log.subsystem %} -{%     set subsystem = ['dmn', 'mgr', 'ike', 'chd','job', 'cfg', 'knl', 'net', 'asn', -                        'enc', 'lib', 'esp', 'tls', 'tnc', 'imc', 'imv', 'pts'] %} -{%   endif %} -{%   set charondebug = subsystem | join (' ' ~ log.level ~ ', ') ~ ' ' ~ log.level %} -{% endif %} -    charondebug = "{{ charondebug }}" -    uniqueids = {{ "no" if disable_uniqreqids is vyos_defined else "yes" }} - -{%  if include_ipsec_conf is vyos_defined %} -include {{ include_ipsec_conf }} -{%  endif %} diff --git a/data/templates/ipsec/ipsec.secrets.tmpl b/data/templates/ipsec/ipsec.secrets.j2 index 865c1ab17..a87ac9bc7 100644 --- a/data/templates/ipsec/ipsec.secrets.tmpl +++ b/data/templates/ipsec/ipsec.secrets.j2 @@ -1,5 +1,5 @@  # Created by VyOS - manual changes will be overwritten -{%  if include_ipsec_secrets is vyos_defined %} +{% if include_ipsec_secrets is vyos_defined %}  include {{ include_ipsec_secrets }} -{%  endif %} +{% endif %} diff --git a/data/templates/ipsec/swanctl.conf.j2 b/data/templates/ipsec/swanctl.conf.j2 new file mode 100644 index 000000000..bf6b8259c --- /dev/null +++ b/data/templates/ipsec/swanctl.conf.j2 @@ -0,0 +1,131 @@ +### Autogenerated by vpn_ipsec.py ### +{% import 'ipsec/swanctl/l2tp.j2' as l2tp_tmpl %} +{% import 'ipsec/swanctl/profile.j2' as profile_tmpl %} +{% import 'ipsec/swanctl/peer.j2' as peer_tmpl %} +{% import 'ipsec/swanctl/remote_access.j2' as remote_access_tmpl %} + +connections { +{% if profile is vyos_defined %} +{%     for name, profile_conf in profile.items() if profile_conf.disable is not vyos_defined and profile_conf.bind.tunnel is vyos_defined %} +{{ profile_tmpl.conn(name, profile_conf, ike_group, esp_group) }} +{%     endfor %} +{% endif %} +{% if site_to_site.peer is vyos_defined %} +{%     for peer, peer_conf in site_to_site.peer.items() if peer not in dhcp_no_address and peer_conf.disable is not vyos_defined %} +{{ peer_tmpl.conn(peer, peer_conf, ike_group, esp_group) }} +{%     endfor %} +{% endif %} +{% if remote_access.connection is vyos_defined %} +{%     for rw, rw_conf in remote_access.connection.items() if rw_conf.disable is not vyos_defined %} +{{ remote_access_tmpl.conn(rw, rw_conf, ike_group, esp_group) }} +{%     endfor %} +{% endif %} +{% if l2tp %} +{{ l2tp_tmpl.conn(l2tp, l2tp_outside_address, l2tp_ike_default, l2tp_esp_default, ike_group, esp_group) }} +{% endif %} +} + +pools { +{% if remote_access.pool is vyos_defined %} +{%     for pool, pool_config in remote_access.pool.items() %} +    {{ pool }} { +{%         if pool_config.prefix is vyos_defined %} +        addrs = {{ pool_config.prefix }} +{%         endif %} +{%         if pool_config.name_server is vyos_defined %} +        dns = {{ pool_config.name_server | join(',') }} +{%         endif %} +{%         if pool_config.exclude is vyos_defined %} +        split_exclude = {{ pool_config.exclude | join(',') }} +{%         endif %} +    } +{%     endfor %} +{% endif %} +} + +secrets { +{% if profile is vyos_defined %} +{%     for name, profile_conf in profile.items() if profile_conf.disable is not vyos_defined and profile_conf.bind.tunnel is vyos_defined %} +{%         if profile_conf.authentication.mode is vyos_defined('pre-shared-secret') %} +{%             for interface in profile_conf.bind.tunnel %} +    ike-dmvpn-{{ interface }} { +        secret = {{ profile_conf.authentication.pre_shared_secret }} +    } +{%             endfor %} +{%         endif %} +{%     endfor %} +{% endif %} +{% if site_to_site.peer is vyos_defined %} +{%     for peer, peer_conf in site_to_site.peer.items() if peer not in dhcp_no_address and peer_conf.disable is not vyos_defined %} +{%         set peer_name = peer.replace("@", "") | dot_colon_to_dash %} +{%         if peer_conf.authentication.mode is vyos_defined('pre-shared-secret') %} +    ike_{{ peer_name }} { +{%             if peer_conf.local_address is vyos_defined %} +        id-local = {{ peer_conf.local_address }} # dhcp:{{ peer_conf.dhcp_interface if 'dhcp_interface' in peer_conf else 'no' }} +{%             endif %} +        id-remote = {{ peer }} +{%             if peer_conf.authentication.id is vyos_defined %} +        id-localid = {{ peer_conf.authentication.id }} +{%             endif %} +{%             if peer_conf.authentication.remote_id is vyos_defined %} +        id-remoteid = {{ peer_conf.authentication.remote_id }} +{%             endif %} +        secret = "{{ peer_conf.authentication.pre_shared_secret }}" +    } +{%         elif peer_conf.authentication.mode is vyos_defined('x509') %} +    private_{{ peer_name }} { +        file = {{ peer_conf.authentication.x509.certificate }}.pem +{%             if peer_conf.authentication.x509.passphrase is vyos_defined %} +        secret = "{{ peer_conf.authentication.x509.passphrase }}" +{%             endif %} +    } +{%         elif peer_conf.authentication.mode is vyos_defined('rsa') %} +    rsa_{{ peer_name }}_local { +        file = {{ peer_conf.authentication.rsa.local_key }}.pem +{%             if peer_conf.authentication.rsa.passphrase is vyos_defined %} +        secret = "{{ peer_conf.authentication.rsa.passphrase }}" +{%             endif %} +    } +{%         endif %} +{%     endfor %} +{% endif %} +{% if remote_access.connection is vyos_defined %} +{%     for ra, ra_conf in remote_access.connection.items() if ra_conf.disable is not vyos_defined %} +{%         if ra_conf.authentication.server_mode is vyos_defined('pre-shared-secret') %} +    ike_{{ ra }} { +{%             if ra_conf.authentication.id is vyos_defined %} +        id = "{{ ra_conf.authentication.id }}" +{%             elif ra_conf.local_address is vyos_defined %} +        id = "{{ ra_conf.local_address }}" +{%             endif %} +        secret = "{{ ra_conf.authentication.pre_shared_secret }}" +    } +{%         endif %} +{%         if ra_conf.authentication.client_mode is vyos_defined('eap-mschapv2') and ra_conf.authentication.local_users.username is vyos_defined %} +{%             for user, user_conf in ra_conf.authentication.local_users.username.items() if user_conf.disable is not vyos_defined %} +    eap-{{ ra }}-{{ user }} { +        secret = "{{ user_conf.password }}" +        id-{{ ra }}-{{ user }} = "{{ user }}" +    } +{%             endfor %} +{%         endif %} +{%     endfor %} +{% endif %} +{% if l2tp %} +{%     if l2tp.authentication.mode is vyos_defined('pre-shared-secret') %} +    ike_l2tp_remote_access { +        id = "{{ l2tp_outside_address }}" +        secret = "{{ l2tp.authentication.pre_shared_secret }}" +    } +{%     elif l2tp.authentication.mode is vyos_defined('x509') %} +    private_l2tp_remote_access { +        id = "{{ l2tp_outside_address }}" +        file = {{ l2tp.authentication.x509.certificate }}.pem +{%         if l2tp.authentication.x509.passphrase is vyos_defined %} +        secret = "{{ l2tp.authentication.x509.passphrase }}" +{%         endif %} +    } +{%     endif %} +{% endif %} +} + diff --git a/data/templates/ipsec/swanctl.conf.tmpl b/data/templates/ipsec/swanctl.conf.tmpl deleted file mode 100644 index 6ba93dd1f..000000000 --- a/data/templates/ipsec/swanctl.conf.tmpl +++ /dev/null @@ -1,131 +0,0 @@ -### Autogenerated by vpn_ipsec.py ### -{% import 'ipsec/swanctl/l2tp.tmpl' as l2tp_tmpl %} -{% import 'ipsec/swanctl/profile.tmpl' as profile_tmpl %} -{% import 'ipsec/swanctl/peer.tmpl' as peer_tmpl %} -{% import 'ipsec/swanctl/remote_access.tmpl' as remote_access_tmpl %} - -connections { -{% if profile is vyos_defined %} -{%   for name, profile_conf in profile.items() if profile_conf.disable is not vyos_defined and profile_conf.bind.tunnel is vyos_defined %} -{{     profile_tmpl.conn(name, profile_conf, ike_group, esp_group) }} -{%   endfor %} -{% endif %} -{% if site_to_site.peer is vyos_defined %} -{%   for peer, peer_conf in site_to_site.peer.items() if peer not in dhcp_no_address and peer_conf.disable is not vyos_defined %} -{{     peer_tmpl.conn(peer, peer_conf, ike_group, esp_group) }} -{%   endfor %} -{%  endif %} -{% if remote_access.connection is vyos_defined %} -{%   for rw, rw_conf in remote_access.connection.items() if rw_conf.disable is not vyos_defined %} -{{ remote_access_tmpl.conn(rw, rw_conf, ike_group, esp_group) }} -{%   endfor %} -{% endif %} -{% if l2tp %} -{{ l2tp_tmpl.conn(l2tp, l2tp_outside_address, l2tp_ike_default, l2tp_esp_default, ike_group, esp_group) }} -{% endif %} -} - -pools { -{%  if remote_access.pool is vyos_defined %} -{%    for pool, pool_config in remote_access.pool.items() %} -    {{ pool }} { -{%      if pool_config.prefix is vyos_defined %} -        addrs = {{ pool_config.prefix }} -{%      endif %} -{%      if pool_config.name_server is vyos_defined %} -        dns = {{ pool_config.name_server | join(',') }} -{%      endif %} -{%      if pool_config.exclude is vyos_defined %} -        split_exclude = {{ pool_config.exclude | join(',') }} -{%      endif %} -    } -{%    endfor %} -{%  endif %} -} - -secrets { -{%  if profile is vyos_defined %} -{%    for name, profile_conf in profile.items() if profile_conf.disable is not vyos_defined and profile_conf.bind.tunnel is vyos_defined %} -{%      if profile_conf.authentication.mode is vyos_defined('pre-shared-secret') %} -{%        for interface in profile_conf.bind.tunnel %} -    ike-dmvpn-{{ interface }} { -        secret = {{ profile_conf.authentication.pre_shared_secret }} -    } -{%        endfor %} -{%      endif %} -{%    endfor %} -{%  endif %} -{%  if site_to_site.peer is vyos_defined %} -{%    for peer, peer_conf in site_to_site.peer.items() if peer not in dhcp_no_address and peer_conf.disable is not vyos_defined %} -{%      set peer_name = peer.replace("@", "") | dot_colon_to_dash %} -{%      if peer_conf.authentication.mode is vyos_defined('pre-shared-secret') %} -    ike_{{ peer_name }} { -{%        if peer_conf.local_address is vyos_defined %} -        id-local = {{ peer_conf.local_address }} # dhcp:{{ peer_conf.dhcp_interface if 'dhcp_interface' in peer_conf else 'no' }} -{%        endif %} -        id-remote = {{ peer }} -{%        if peer_conf.authentication.id is vyos_defined %} -        id-localid = {{ peer_conf.authentication.id }} -{%        endif %} -{%        if peer_conf.authentication.remote_id is vyos_defined %} -        id-remoteid = {{ peer_conf.authentication.remote_id }} -{%        endif %} -        secret = "{{ peer_conf.authentication.pre_shared_secret }}" -    } -{%      elif peer_conf.authentication.mode is vyos_defined('x509') %} -    private_{{ peer_name }} { -        file = {{ peer_conf.authentication.x509.certificate }}.pem -{%        if peer_conf.authentication.x509.passphrase is vyos_defined %} -        secret = "{{ peer_conf.authentication.x509.passphrase }}" -{%        endif %} -    } -{%      elif peer_conf.authentication.mode is vyos_defined('rsa') %} -    rsa_{{ peer_name }}_local { -        file = {{ peer_conf.authentication.rsa.local_key }}.pem -{%        if peer_conf.authentication.rsa.passphrase is vyos_defined %} -        secret = "{{ peer_conf.authentication.rsa.passphrase }}" -{%        endif %} -    } -{%      endif %} -{%    endfor %} -{%  endif %} -{%  if remote_access.connection is vyos_defined %} -{%    for ra, ra_conf in remote_access.connection.items() if ra_conf.disable is not vyos_defined %} -{%      if ra_conf.authentication.server_mode is vyos_defined('pre-shared-secret') %} -    ike_{{ ra }} { -{%        if ra_conf.authentication.id is vyos_defined %} -        id = "{{ ra_conf.authentication.id }}" -{%        elif ra_conf.local_address is vyos_defined %} -        id = "{{ ra_conf.local_address }}" -{%        endif %} -        secret = "{{ ra_conf.authentication.pre_shared_secret }}" -    } -{%      endif %} -{%      if ra_conf.authentication.client_mode is vyos_defined('eap-mschapv2') and ra_conf.authentication.local_users.username is vyos_defined %} -{%        for user, user_conf in ra_conf.authentication.local_users.username.items() if user_conf.disable is not vyos_defined %} -    eap-{{ ra }}-{{ user }} { -        secret = "{{ user_conf.password }}" -        id-{{ ra }}-{{ user }} = "{{ user }}" -    } -{%        endfor %} -{%      endif %} -{%    endfor %} -{%  endif %} -{%  if l2tp %} -{%    if l2tp.authentication.mode is vyos_defined('pre-shared-secret') %} -    ike_l2tp_remote_access { -        id = "{{ l2tp_outside_address }}" -        secret = "{{ l2tp.authentication.pre_shared_secret }}" -    } -{%    elif l2tp.authentication.mode is vyos_defined('x509') %} -    private_l2tp_remote_access { -        id = "{{ l2tp_outside_address }}" -        file = {{ l2tp.authentication.x509.certificate }}.pem -{%      if l2tp.authentication.x509.passphrase is vyos_defined %} -        secret = "{{ l2tp.authentication.x509.passphrase }}" -{%      endif %} -    } -{%    endif %} -{%  endif %} -} - diff --git a/data/templates/ipsec/swanctl/l2tp.tmpl b/data/templates/ipsec/swanctl/l2tp.j2 index c0e81e0aa..7e63865cc 100644 --- a/data/templates/ipsec/swanctl/l2tp.tmpl +++ b/data/templates/ipsec/swanctl/l2tp.j2 @@ -1,6 +1,6 @@  {% macro conn(l2tp, l2tp_outside_address, l2tp_ike_default, l2tp_esp_default, ike_group, esp_group) %} -{%   set l2tp_ike = ike_group[l2tp.ike_group] if l2tp.ike_group is vyos_defined else None %} -{%   set l2tp_esp = esp_group[l2tp.esp_group] if l2tp.esp_group is vyos_defined else None %} +{% set l2tp_ike = ike_group[l2tp.ike_group] if l2tp.ike_group is vyos_defined else None %} +{% set l2tp_esp = esp_group[l2tp.esp_group] if l2tp.esp_group is vyos_defined else None %}      l2tp_remote_access {          proposals = {{ l2tp_ike | get_esp_ike_cipher | join(',') if l2tp_ike else l2tp_ike_default }}          local_addrs = {{ l2tp_outside_address }} @@ -10,9 +10,9 @@          reauth_time = 0          local {              auth = {{ 'psk' if l2tp.authentication.mode == 'pre-shared-secret' else 'pubkey' }} -{%   if l2tp.authentication.mode == 'x509' %} +{% if l2tp.authentication.mode == 'x509' %}              certs = {{ l2tp.authentication.x509.certificate }}.pem -{%   endif %} +{% endif %}          }          remote {              auth = {{ 'psk' if l2tp.authentication.mode == 'pre-shared-secret' else 'pubkey' }} diff --git a/data/templates/ipsec/swanctl/peer.tmpl b/data/templates/ipsec/swanctl/peer.j2 index 61af85ed4..90d2c774f 100644 --- a/data/templates/ipsec/swanctl/peer.tmpl +++ b/data/templates/ipsec/swanctl/peer.j2 @@ -1,78 +1,78 @@  {% macro conn(peer, peer_conf, ike_group, esp_group) %} -{%   set name = peer.replace("@", "") | dot_colon_to_dash %} -{#   peer needs to reference the global IKE configuration for certain values #} -{%   set ike = ike_group[peer_conf.ike_group] %} +{% set name = peer.replace("@", "") | dot_colon_to_dash %} +{# peer needs to reference the global IKE configuration for certain values #} +{% set ike = ike_group[peer_conf.ike_group] %}      peer_{{ name }} {          proposals = {{ ike | get_esp_ike_cipher | join(',') }}          version = {{ ike.key_exchange[4:] if ike.key_exchange is vyos_defined else "0" }} -{%   if peer_conf.virtual_address is vyos_defined %} +{% if peer_conf.virtual_address is vyos_defined %}          vips = {{ peer_conf.virtual_address | join(', ') }} -{%   endif %} +{% endif %}          local_addrs = {{ peer_conf.local_address if peer_conf.local_address != 'any' else '0.0.0.0/0' }} # dhcp:{{ peer_conf.dhcp_interface if 'dhcp_interface' in peer_conf else 'no' }}          remote_addrs = {{ peer if peer not in ['any', '0.0.0.0'] and peer[0:1] != '@' else '0.0.0.0/0' }} -{%   if peer_conf.authentication.mode is vyos_defined('x509') %} +{% if peer_conf.authentication.mode is vyos_defined('x509') %}          send_cert = always -{%   endif %} -{%   if ike.dead_peer_detection is vyos_defined %} +{% endif %} +{% if ike.dead_peer_detection is vyos_defined %}          dpd_timeout = {{ ike.dead_peer_detection.timeout }}          dpd_delay = {{ ike.dead_peer_detection.interval }} -{%   endif %} -{%   if ike.key_exchange is vyos_defined('ikev1') and ike.mode is vyos_defined('aggressive') %} +{% endif %} +{% if ike.key_exchange is vyos_defined('ikev1') and ike.mode is vyos_defined('aggressive') %}          aggressive = yes -{%   endif %} +{% endif %}          rekey_time = {{ ike.lifetime }}s          mobike = {{ "yes" if ike.mobike is not defined or ike.mobike == "enable" else "no" }} -{%   if peer[0:1] == '@' %} +{% if peer[0:1] == '@' %}          keyingtries = 0          reauth_time = 0 -{%   elif peer_conf.connection_type is not vyos_defined or peer_conf.connection_type is vyos_defined('initiate') %} +{% elif peer_conf.connection_type is not vyos_defined or peer_conf.connection_type is vyos_defined('initiate') %}          keyingtries = 0 -{%   elif peer_conf.connection_type is vyos_defined('respond') %} +{% elif peer_conf.connection_type is vyos_defined('respond') %}          keyingtries = 1 -{%   endif %} -{%   if peer_conf.force_encapsulation is vyos_defined('enable') %} +{% endif %} +{% if peer_conf.force_encapsulation is vyos_defined('enable') %}          encap = yes -{%   endif %} +{% endif %}          local { -{%   if peer_conf.authentication.id is vyos_defined %} +{% if peer_conf.authentication.id is vyos_defined %}              id = "{{ peer_conf.authentication.id }}" -{%   endif %} +{% endif %}              auth = {{ 'psk' if peer_conf.authentication.mode == 'pre-shared-secret' else 'pubkey' }} -{%   if peer_conf.authentication.mode == 'x509' %} +{% if peer_conf.authentication.mode == 'x509' %}              certs = {{ peer_conf.authentication.x509.certificate }}.pem -{%   elif peer_conf.authentication.mode == 'rsa' %} +{% elif peer_conf.authentication.mode == 'rsa' %}              pubkeys = {{ peer_conf.authentication.rsa.local_key }}.pem -{%   endif %} +{% endif %}          }          remote { -{%   if peer_conf.authentication.remote_id is vyos_defined %} +{% if peer_conf.authentication.remote_id is vyos_defined %}              id = "{{ peer_conf.authentication.remote_id }}" -{%   else %} +{% else %}              id = "{{ peer }}" -{%   endif %} +{% endif %}              auth = {{ 'psk' if peer_conf.authentication.mode == 'pre-shared-secret' else 'pubkey' }} -{%   if peer_conf.authentication.mode == 'rsa' %} +{% if peer_conf.authentication.mode == 'rsa' %}              pubkeys = {{ peer_conf.authentication.rsa.remote_key }}.pem -{%   endif %} +{% endif %}          }          children { -{%   if peer_conf.vti.bind is vyos_defined and peer_conf.tunnel is not vyos_defined %} +{% if peer_conf.vti.bind is vyos_defined and peer_conf.tunnel is not vyos_defined %}  {%     set vti_esp = esp_group[ peer_conf.vti.esp_group ] if peer_conf.vti.esp_group is vyos_defined else esp_group[ peer_conf.default_esp_group ] %}              peer_{{ name }}_vti {                  esp_proposals = {{ vti_esp | get_esp_ike_cipher(ike) | join(',') }} -{%   if vti_esp.life_bytes is vyos_defined %} +{%     if vti_esp.life_bytes is vyos_defined %}                  life_bytes = {{ vti_esp.life_bytes }} -{%   endif %} -{%   if vti_esp.life_packets is vyos_defined %} +{%     endif %} +{%     if vti_esp.life_packets is vyos_defined %}                  life_packets = {{ vti_esp.life_packets }} -{%   endif %} +{%     endif %}                  life_time = {{ vti_esp.lifetime }}s                  local_ts = 0.0.0.0/0,::/0                  remote_ts = 0.0.0.0/0,::/0                  updown = "/etc/ipsec.d/vti-up-down {{ peer_conf.vti.bind }}" -                {# The key defaults to 0 and will match any policies which similarly do not have a lookup key configuration. #} -                {# Thus we simply shift the key by one to also support a vti0 interface #} -{%              set if_id = peer_conf.vti.bind | replace('vti', '') | int +1 %} +{#              The key defaults to 0 and will match any policies which similarly do not have a lookup key configuration. #} +{#              Thus we simply shift the key by one to also support a vti0 interface #} +{%     set if_id = peer_conf.vti.bind | replace('vti', '') | int + 1 %}                  if_id_in = {{ if_id }}                  if_id_out = {{ if_id }}                  ipcomp = {{ 'yes' if vti_esp.compression is vyos_defined('enable') else 'no' }} @@ -87,80 +87,80 @@                  start_action = none  {%     endif %}  {%     if ike.dead_peer_detection is vyos_defined %} -{%       set dpd_translate = {'clear': 'clear', 'hold': 'trap', 'restart': 'restart'} %} +{%         set dpd_translate = {'clear': 'clear', 'hold': 'trap', 'restart': 'restart'} %}                  dpd_action = {{ dpd_translate[ike.dead_peer_detection.action] }}  {%     endif %}                  close_action = {{ {'none': 'none', 'hold': 'trap', 'restart': 'start'}[ike.close_action] }}              } -{%   elif peer_conf.tunnel is vyos_defined %} +{% elif peer_conf.tunnel is vyos_defined %}  {%     for tunnel_id, tunnel_conf in peer_conf.tunnel.items() if tunnel_conf.disable is not defined %} -{%       set tunnel_esp_name = tunnel_conf.esp_group if tunnel_conf.esp_group is vyos_defined else peer_conf.default_esp_group %} -{%       set tunnel_esp = esp_group[tunnel_esp_name] %} -{%       set proto = tunnel_conf.protocol if tunnel_conf.protocol is vyos_defined else '' %} -{%       set local_port = tunnel_conf.local.port if tunnel_conf.local.port is vyos_defined else '' %} -{%       set local_suffix = '[{0}/{1}]'.format(proto, local_port) if proto or local_port else '' %} -{%       set remote_port = tunnel_conf.remote.port if tunnel_conf.remote.port is vyos_defined else '' %} -{%       set remote_suffix = '[{0}/{1}]'.format(proto, remote_port) if proto or remote_port else '' %} +{%         set tunnel_esp_name = tunnel_conf.esp_group if tunnel_conf.esp_group is vyos_defined else peer_conf.default_esp_group %} +{%         set tunnel_esp = esp_group[tunnel_esp_name] %} +{%         set proto = tunnel_conf.protocol if tunnel_conf.protocol is vyos_defined else '' %} +{%         set local_port = tunnel_conf.local.port if tunnel_conf.local.port is vyos_defined else '' %} +{%         set local_suffix = '[{0}/{1}]'.format(proto, local_port) if proto or local_port else '' %} +{%         set remote_port = tunnel_conf.remote.port if tunnel_conf.remote.port is vyos_defined else '' %} +{%         set remote_suffix = '[{0}/{1}]'.format(proto, remote_port) if proto or remote_port else '' %}              peer_{{ name }}_tunnel_{{ tunnel_id }} {                  esp_proposals = {{ tunnel_esp | get_esp_ike_cipher(ike) | join(',') }} -{%       if tunnel_esp.life_bytes is vyos_defined %} +{%         if tunnel_esp.life_bytes is vyos_defined %}                  life_bytes = {{ tunnel_esp.life_bytes }} -{%       endif %} -{%       if tunnel_esp.life_packets is vyos_defined %} +{%         endif %} +{%         if tunnel_esp.life_packets is vyos_defined %}                  life_packets = {{ tunnel_esp.life_packets }} -{%       endif %} +{%         endif %}                  life_time = {{ tunnel_esp.lifetime }}s -{%       if tunnel_esp.mode is not defined or tunnel_esp.mode == 'tunnel' %} -{%         if tunnel_conf.local.prefix is vyos_defined %} -{%           set local_prefix = tunnel_conf.local.prefix if 'any' not in tunnel_conf.local.prefix else ['0.0.0.0/0', '::/0'] %} +{%         if tunnel_esp.mode is not defined or tunnel_esp.mode == 'tunnel' %} +{%             if tunnel_conf.local.prefix is vyos_defined %} +{%                 set local_prefix = tunnel_conf.local.prefix if 'any' not in tunnel_conf.local.prefix else ['0.0.0.0/0', '::/0'] %}                  local_ts = {{ local_prefix | join(local_suffix + ",") }}{{ local_suffix }} -{%         endif %} -{%         if tunnel_conf.remote.prefix is vyos_defined %} -{%           set remote_prefix = tunnel_conf.remote.prefix if 'any' not in tunnel_conf.remote.prefix else ['0.0.0.0/0', '::/0'] %} +{%             endif %} +{%             if tunnel_conf.remote.prefix is vyos_defined %} +{%                 set remote_prefix = tunnel_conf.remote.prefix if 'any' not in tunnel_conf.remote.prefix else ['0.0.0.0/0', '::/0'] %}                  remote_ts = {{ remote_prefix | join(remote_suffix + ",") }}{{ remote_suffix }} -{%         endif %} -{%         if tunnel_conf.priority is vyos_defined %} +{%             endif %} +{%             if tunnel_conf.priority is vyos_defined %}                  priority = {{ tunnel_conf.priority }} -{%         endif %} -{%       elif tunnel_esp.mode == 'transport' %} +{%             endif %} +{%         elif tunnel_esp.mode == 'transport' %}                  local_ts = {{ peer_conf.local_address }}{{ local_suffix }}                  remote_ts = {{ peer }}{{ remote_suffix }} -{%       endif %} +{%         endif %}                  ipcomp = {{ 'yes' if tunnel_esp.compression is vyos_defined('enable') else 'no' }}                  mode = {{ tunnel_esp.mode }} -{%       if peer[0:1] == '@' %} +{%         if peer[0:1] == '@' %}                  start_action = none -{%       elif peer_conf.connection_type is not vyos_defined or peer_conf.connection_type is vyos_defined('initiate') %} +{%         elif peer_conf.connection_type is not vyos_defined or peer_conf.connection_type is vyos_defined('initiate') %}                  start_action = start -{%       elif peer_conf.connection_type is vyos_defined('respond') %} +{%         elif peer_conf.connection_type is vyos_defined('respond') %}                  start_action = trap -{%       elif peer_conf.connection_type is vyos_defined('none') %} +{%         elif peer_conf.connection_type is vyos_defined('none') %}                  start_action = none -{%       endif %} -{%       if ike.dead_peer_detection is vyos_defined %} -{%         set dpd_translate = {'clear': 'clear', 'hold': 'trap', 'restart': 'restart'} %} +{%         endif %} +{%         if ike.dead_peer_detection is vyos_defined %} +{%             set dpd_translate = {'clear': 'clear', 'hold': 'trap', 'restart': 'restart'} %}                  dpd_action = {{ dpd_translate[ike.dead_peer_detection.action] }} -{%       endif %} +{%         endif %}                  close_action = {{ {'none': 'none', 'hold': 'trap', 'restart': 'start'}[ike.close_action] }} -{%       if peer_conf.vti.bind is vyos_defined %} +{%         if peer_conf.vti.bind is vyos_defined %} +{#             The key defaults to 0 and will match any policies which similarly do not have a lookup key configuration. #} +{#             Thus we simply shift the key by one to also support a vti0 interface #} +{%             set if_id = peer_conf.vti.bind | replace('vti', '') | int + 1 %}                  updown = "/etc/ipsec.d/vti-up-down {{ peer_conf.vti.bind }}" -                {# The key defaults to 0 and will match any policies which similarly do not have a lookup key configuration. #} -                {# Thus we simply shift the key by one to also support a vti0 interface #} -{%              set if_id = peer_conf.vti.bind | replace('vti', '') | int +1 %}                  if_id_in = {{ if_id }}                  if_id_out = {{ if_id }} -{%       endif %} +{%         endif %}              } -{%       if tunnel_conf.passthrough is vyos_defined %} +{%         if tunnel_conf.passthrough is vyos_defined %}              peer_{{ name }}_tunnel_{{ tunnel_id }}_passthrough {                  local_ts = {{ tunnel_conf.passthrough | join(",") }}                  remote_ts = {{ tunnel_conf.passthrough | join(",") }}                  start_action = trap                  mode = pass              } -{%       endif %} +{%         endif %}  {%     endfor %} -{%   endif %} +{% endif %}          }      }  {% endmacro %} diff --git a/data/templates/ipsec/swanctl/profile.tmpl b/data/templates/ipsec/swanctl/profile.j2 index 0f1c2fda2..d4f417378 100644 --- a/data/templates/ipsec/swanctl/profile.tmpl +++ b/data/templates/ipsec/swanctl/profile.j2 @@ -1,39 +1,39 @@  {% macro conn(name, profile_conf, ike_group, esp_group) %} -{#   peer needs to reference the global IKE configuration for certain values #} -{%   set ike = ike_group[profile_conf.ike_group] %} -{%   set esp = esp_group[profile_conf.esp_group] %} -{%   if profile_conf.bind.tunnel is vyos_defined %} +{# peer needs to reference the global IKE configuration for certain values #} +{% set ike = ike_group[profile_conf.ike_group] %} +{% set esp = esp_group[profile_conf.esp_group] %} +{% if profile_conf.bind.tunnel is vyos_defined %}  {%     for interface in profile_conf.bind.tunnel %}      dmvpn-{{ name }}-{{ interface }} {          proposals = {{ ike_group[profile_conf.ike_group] | get_esp_ike_cipher | join(',') }}          version = {{ ike.key_exchange[4:] if ike.key_exchange is vyos_defined else "0" }}          rekey_time = {{ ike.lifetime }}s          keyingtries = 0 -{%       if profile_conf.authentication.mode is vyos_defined('pre-shared-secret') %} +{%         if profile_conf.authentication.mode is vyos_defined('pre-shared-secret') %}          local {              auth = psk          }          remote {              auth = psk          } -{%       endif %} +{%         endif %}          children {              dmvpn { -                esp_proposals = {{ esp | get_esp_ike_cipher(ike) | join(',')  }} +                esp_proposals = {{ esp | get_esp_ike_cipher(ike) | join(',') }}                  rekey_time = {{ esp.lifetime }}s                  rand_time = 540s                  local_ts = dynamic[gre]                  remote_ts = dynamic[gre]                  mode = {{ esp.mode }} -{%       if ike.dead_peer_detection.action is vyos_defined %} +{%         if ike.dead_peer_detection.action is vyos_defined %}                  dpd_action = {{ ike.dead_peer_detection.action }} -{%       endif %} -{%       if esp.compression is vyos_defined('enable') %} +{%         endif %} +{%         if esp.compression is vyos_defined('enable') %}                  ipcomp = yes -{%       endif %} +{%         endif %}              }          }      }  {%     endfor %} -{%   endif %} +{% endif %}  {% endmacro %} diff --git a/data/templates/ipsec/swanctl/remote_access.tmpl b/data/templates/ipsec/swanctl/remote_access.j2 index 059984139..d2760ec1f 100644 --- a/data/templates/ipsec/swanctl/remote_access.tmpl +++ b/data/templates/ipsec/swanctl/remote_access.j2 @@ -1,7 +1,7 @@  {% macro conn(name, rw_conf, ike_group, esp_group) %} -{#   peer needs to reference the global IKE configuration for certain values #} -{%   set ike = ike_group[rw_conf.ike_group] %} -{%   set esp = esp_group[rw_conf.esp_group] %} +{# peer needs to reference the global IKE configuration for certain values #} +{% set ike = ike_group[rw_conf.ike_group] %} +{% set esp = esp_group[rw_conf.esp_group] %}      ra-{{ name }} {          remote_addrs = %any          local_addrs = {{ rw_conf.local_address if rw_conf.local_address is vyos_defined else '%any' }} @@ -10,28 +10,29 @@          send_certreq = no          rekey_time = {{ ike.lifetime }}s          keyingtries = 0 -{%   if rw_conf.unique is vyos_defined %} +{% if rw_conf.unique is vyos_defined %}          unique = {{ rw_conf.unique }} -{%   endif %} -{%   if rw_conf.pool is vyos_defined %} +{% endif %} +{% if rw_conf.pool is vyos_defined %}          pools = {{ rw_conf.pool | join(',') }} -{%   endif %} +{% endif %}          local { -{%   if rw_conf.authentication.id is vyos_defined and rw_conf.authentication.use_x509_id is not vyos_defined %} -            id = '{{ rw_conf.authentication.id }}' -{%   endif %} -{%   if rw_conf.authentication.server_mode == 'x509' %} +{% if rw_conf.authentication.id is vyos_defined and rw_conf.authentication.use_x509_id is not vyos_defined %} +{#          please use " quotes - else Apple iOS goes crazy #} +            id = "{{ rw_conf.authentication.id }}" +{% endif %} +{% if rw_conf.authentication.server_mode == 'x509' %}              auth = pubkey              certs = {{ rw_conf.authentication.x509.certificate }}.pem -{%   elif rw_conf.authentication.server_mode == 'pre-shared-secret' %} +{% elif rw_conf.authentication.server_mode == 'pre-shared-secret' %}              auth = psk -{%   endif %} +{% endif %}          }          remote {              auth = {{ rw_conf.authentication.client_mode }} -{%   if rw_conf.authentication.client_mode.startswith("eap") %} +{% if rw_conf.authentication.client_mode.startswith("eap") %}              eap_id = %any -{%   endif %} +{% endif %}          }          children {              ikev2-vpn  { @@ -40,9 +41,9 @@                  rand_time = 540s                  dpd_action = clear                  inactivity = {{ rw_conf.timeout }} -{%   set local_prefix = rw_conf.local.prefix if rw_conf.local.prefix is vyos_defined else ['0.0.0.0/0', '::/0'] %} -{%   set local_port = rw_conf.local.port if rw_conf.local.port is vyos_defined else '' %} -{%   set local_suffix = '[%any/{1}]'.format(local_port) if local_port else '' %} +{% set local_prefix = rw_conf.local.prefix if rw_conf.local.prefix is vyos_defined else ['0.0.0.0/0', '::/0'] %} +{% set local_port = rw_conf.local.port if rw_conf.local.port is vyos_defined else '' %} +{% set local_suffix = '[%any/{1}]'.format(local_port) if local_port else '' %}                  local_ts = {{ local_prefix | join(local_suffix + ",") }}{{ local_suffix }}              }          } diff --git a/data/templates/ipsec/windows_profile.tmpl b/data/templates/ipsec/windows_profile.j2 index 8c26944be..8c26944be 100644 --- a/data/templates/ipsec/windows_profile.tmpl +++ b/data/templates/ipsec/windows_profile.j2 diff --git a/data/templates/lcd/LCDd.conf.tmpl b/data/templates/lcd/LCDd.conf.j2 index 2c8c6602d..3631add1d 100644 --- a/data/templates/lcd/LCDd.conf.tmpl +++ b/data/templates/lcd/LCDd.conf.j2 @@ -49,13 +49,13 @@ DriverPath=/usr/lib/x86_64-linux-gnu/lcdproc/  #   text, tyan, ula200, vlsys_m428, xosd, yard2LCD  {% if model is vyos_defined %} -{%   if model.startswith('cfa-') %} +{%     if model.startswith('cfa-') %}  Driver=CFontzPacket -{%   elif model == 'sdec' %} +{%     elif model == 'sdec' %}  Driver=sdeclcd -{%   elif model == 'hd44780' %} +{%     elif model == 'hd44780' %}  Driver=hd44780 -{%   endif %} +{%     endif %}  {% endif %}  # Tells the driver to bind to the given interface. [default: 127.0.0.1] @@ -116,7 +116,7 @@ Heartbeat=off  TitleSpeed=10  {% if model is vyos_defined %} -{%   if model.startswith('cfa-') %} +{%     if model.startswith('cfa-') %}  ## CrystalFontz packet driver (for CFA533, CFA631, CFA633 & CFA635) ##  [CFontzPacket]  Model={{ model.split('-')[1] }} @@ -126,14 +126,14 @@ Brightness=500  OffBrightness=50  Reboot=yes  USB=yes -{%   elif model == 'sdec' %} +{%     elif model == 'sdec' %}  ## SDEC driver for Lanner, Watchguard, Sophos sppliances ##  [sdeclcd]  # No options -{%   elif model == 'hd44780' %} +{%     elif model == 'hd44780' %}  [hd44780]  ConnectionType=ezio  Device={{ device }}  Size=16x2 -{%   endif %} +{%     endif %}  {% endif %} diff --git a/data/templates/lcd/lcdproc.conf.tmpl b/data/templates/lcd/lcdproc.conf.j2 index c79f3cd0d..c79f3cd0d 100644 --- a/data/templates/lcd/lcdproc.conf.tmpl +++ b/data/templates/lcd/lcdproc.conf.j2 diff --git a/data/templates/lldp/lldpd.j2 b/data/templates/lldp/lldpd.j2 new file mode 100644 index 000000000..3c499197d --- /dev/null +++ b/data/templates/lldp/lldpd.j2 @@ -0,0 +1,2 @@ +### Autogenerated by lldp.py ### +DAEMON_ARGS="-M 4 {{ '-x' if snmp.enable is vyos_defined }} {{ '-c' if legacy_protocols.cdp is vyos_defined }} {{ '-e' if legacy_protocols.edp is vyos_defined }} {{ '-f' if legacy_protocols.fdp is vyos_defined }} {{ '-s' if legacy_protocols.sonmp is vyos_defined }}" diff --git a/data/templates/lldp/lldpd.tmpl b/data/templates/lldp/lldpd.tmpl deleted file mode 100644 index 9ab1e4367..000000000 --- a/data/templates/lldp/lldpd.tmpl +++ /dev/null @@ -1,2 +0,0 @@ -### Autogenerated by lldp.py ### -DAEMON_ARGS="-M 4{% if snmp.enable is vyos_defined %} -x{% endif %}{% if legacy_protocols.cdp is vyos_defined %} -c{% endif %}{% if legacy_protocols.edp is vyos_defined %} -e{% endif %}{% if legacy_protocols.fdp is vyos_defined %} -f{% endif %}{% if legacy_protocols.sonmp is vyos_defined %} -s{% endif %}" diff --git a/data/templates/lldp/vyos.conf.tmpl b/data/templates/lldp/vyos.conf.j2 index c34a851aa..ec84231d8 100644 --- a/data/templates/lldp/vyos.conf.tmpl +++ b/data/templates/lldp/vyos.conf.j2 @@ -3,21 +3,21 @@  configure system platform VyOS  configure system description "VyOS {{ version }}"  {% if interface is vyos_defined %} -{%   set tmp = [] %} -{%   for iface, iface_options in interface.items() if not iface_options.disable %} -{%     if iface == 'all' %} -{%       set iface = '*' %} -{%     endif %} -{%     set _ = tmp.append(iface) %} -{%     if iface_options.location is vyos_defined %} -{%       if iface_options.location.elin is vyos_defined %} +{%     set tmp = [] %} +{%     for iface, iface_options in interface.items() if not iface_options.disable %} +{%         if iface == 'all' %} +{%             set iface = '*' %} +{%         endif %} +{%         set _ = tmp.append(iface) %} +{%         if iface_options.location is vyos_defined %} +{%             if iface_options.location.elin is vyos_defined %}  configure ports {{ iface }} med location elin "{{ iface_options.location.elin }}" -{%       endif %} -{%       if iface_options.location.coordinate_based is vyos_defined %} +{%             endif %} +{%             if iface_options.location.coordinate_based is vyos_defined %}  configure ports {{ iface }} med location coordinate latitude "{{ iface_options.location.coordinate_based.latitude }}" longitude "{{ iface_options.location.coordinate_based.longitude }}" altitude "{{ iface_options.location.coordinate_based.altitude }}m" datum "{{ iface_options.location.coordinate_based.datum }}" -{%       endif %} -{%     endif %} -{%   endfor %} +{%             endif %} +{%         endif %} +{%     endfor %}  configure system interface pattern "{{ tmp | join(",") }}"  {% endif %}  {% if management_address is vyos_defined %} diff --git a/data/templates/login/authorized_keys.tmpl b/data/templates/login/authorized_keys.j2 index 9402c8719..aabca47cf 100644 --- a/data/templates/login/authorized_keys.tmpl +++ b/data/templates/login/authorized_keys.j2 @@ -1,9 +1,9 @@  ### Automatically generated by system-login.py ###  {% if authentication.public_keys is vyos_defined %} -{%   for key, key_options in authentication.public_keys.items() %} +{%     for key, key_options in authentication.public_keys.items() %}  {# The whitespace after options is wisely chosen #}  {{ key_options.options ~ ' ' if key_options.options is vyos_defined }}{{ key_options.type }} {{ key_options.key }} {{ key }} -{%   endfor %} +{%     endfor %}  {% endif %} diff --git a/data/templates/login/pam_radius_auth.conf.j2 b/data/templates/login/pam_radius_auth.conf.j2 new file mode 100644 index 000000000..1105b60e5 --- /dev/null +++ b/data/templates/login/pam_radius_auth.conf.j2 @@ -0,0 +1,36 @@ +# Automatically generated by system-login.py +# RADIUS configuration file + +{% if radius is vyos_defined %} +{#     RADIUS IPv6 source address must be specified in [] notation #} +{%     set source_address = namespace()  %} +{%     if radius.source_address is vyos_defined %} +{%         for address in radius.source_address %} +{%             if address | is_ipv4 %} +{%                 set source_address.ipv4 = address %} +{%             elif address | is_ipv6 %} +{%                 set source_address.ipv6 = "[" + address + "]" %} +{%             endif %} +{%         endfor %} +{%     endif %} +{%     if radius.server is vyos_defined %} +# server[:port]        shared_secret             timeout    source_ip +{# .items() returns a tuple of two elements: key and value. 1 relates to the 2nd element i.e. the value and .priority relates to the key from the internal dict #} +{%         for server, options in radius.server.items() | sort(attribute='1.priority') if not options.disabled %} +{#         RADIUS IPv6 servers must be specified in [] notation #} +{%             if server | is_ipv4 %} +{{ server }}:{{ options.port }} {{ "%-25s" | format(options.key) }} {{ "%-10s" | format(options.timeout) }} {{ source_address.ipv4 if source_address.ipv4 is vyos_defined }} +{%             else %} +[{{ server }}]:{{ options.port }} {{ "%-25s" | format(options.key) }} {{ "%-10s" | format(options.timeout) }} {{ source_address.ipv6 if source_address.ipv6 is vyos_defined }} +{%             endif %} +{%         endfor %} +{%     endif %} + +priv-lvl 15 +mapped_priv_user radius_priv_user + +{%     if radius.vrf is vyos_defined %} +vrf-name {{ radius.vrf }} +{%     endif %} +{% endif %} + diff --git a/data/templates/login/pam_radius_auth.conf.tmpl b/data/templates/login/pam_radius_auth.conf.tmpl deleted file mode 100644 index 4e34ade41..000000000 --- a/data/templates/login/pam_radius_auth.conf.tmpl +++ /dev/null @@ -1,36 +0,0 @@ -# Automatically generated by system-login.py -# RADIUS configuration file - -{% if radius is vyos_defined %} -{#   RADIUS IPv6 source address must be specified in [] notation #} -{%   set source_address = namespace()  %} -{%   if radius.source_address is vyos_defined %} -{%     for address in radius.source_address %} -{%       if address | is_ipv4 %} -{%         set source_address.ipv4 = address %} -{%       elif address | is_ipv6 %} -{%         set source_address.ipv6 = "[" + address + "]" %} -{%       endif %} -{%     endfor %} -{%   endif %} -{% if radius.server is vyos_defined %} -# server[:port]        shared_secret             timeout    source_ip -{# .items() returns a tuple of two elements: key and value. 1 relates to the 2nd element i.e. the value and .priority relates to the key from the internal dict #} -{%   for server, options in radius.server.items() | sort(attribute='1.priority') if not options.disabled %} -{#   RADIUS IPv6 servers must be specified in [] notation #} -{%     if server | is_ipv4 %} -{{ server }}:{{ options.port }} {{ "%-25s" | format(options.key) }} {{ "%-10s" | format(options.timeout) }} {{ source_address.ipv4 if source_address.ipv4 is vyos_defined }} -{%     else %} -[{{ server }}]:{{ options.port }} {{ "%-25s" | format(options.key) }} {{ "%-10s" | format(options.timeout) }} {{ source_address.ipv6 if source_address.ipv6 is vyos_defined }} -{%     endif %} -{%   endfor %} -{% endif %} - -priv-lvl 15 -mapped_priv_user radius_priv_user - -{%   if radius.vrf is vyos_defined %} -vrf-name {{ radius.vrf }} -{%   endif %} -{% endif %} - diff --git a/data/templates/logs/logrotate/vyos-atop.tmpl b/data/templates/logs/logrotate/vyos-atop.j2 index 2d078f379..2d078f379 100644 --- a/data/templates/logs/logrotate/vyos-atop.tmpl +++ b/data/templates/logs/logrotate/vyos-atop.j2 diff --git a/data/templates/logs/logrotate/vyos-rsyslog.tmpl b/data/templates/logs/logrotate/vyos-rsyslog.j2 index f2e4d2ab2..f2e4d2ab2 100644 --- a/data/templates/logs/logrotate/vyos-rsyslog.tmpl +++ b/data/templates/logs/logrotate/vyos-rsyslog.j2 diff --git a/data/templates/mdns-repeater/avahi-daemon.tmpl b/data/templates/mdns-repeater/avahi-daemon.j2 index 65bb5a306..65bb5a306 100644 --- a/data/templates/mdns-repeater/avahi-daemon.tmpl +++ b/data/templates/mdns-repeater/avahi-daemon.j2 diff --git a/data/templates/monitoring/override.conf.tmpl b/data/templates/monitoring/override.conf.j2 index f8f150791..f8f150791 100644 --- a/data/templates/monitoring/override.conf.tmpl +++ b/data/templates/monitoring/override.conf.j2 diff --git a/data/templates/monitoring/syslog_telegraf.tmpl b/data/templates/monitoring/syslog_telegraf.j2 index cdcbd92a4..cdcbd92a4 100644 --- a/data/templates/monitoring/syslog_telegraf.tmpl +++ b/data/templates/monitoring/syslog_telegraf.j2 diff --git a/data/templates/monitoring/systemd_vyos_telegraf_service.tmpl b/data/templates/monitoring/systemd_vyos_telegraf_service.j2 index 234ef5586..234ef5586 100644 --- a/data/templates/monitoring/systemd_vyos_telegraf_service.tmpl +++ b/data/templates/monitoring/systemd_vyos_telegraf_service.j2 diff --git a/data/templates/monitoring/telegraf.j2 b/data/templates/monitoring/telegraf.j2 new file mode 100644 index 000000000..a732fb5de --- /dev/null +++ b/data/templates/monitoring/telegraf.j2 @@ -0,0 +1,122 @@ +# Generated by /usr/libexec/vyos/conf_mode/service_monitoring_telegraf.py + +[agent] +  interval = "15s" +  round_interval = true +  metric_batch_size = 1000 +  metric_buffer_limit = 10000 +  collection_jitter = "5s" +  flush_interval = "15s" +  flush_jitter = "0s" +  precision = "" +  debug = false +  quiet = false +  logfile = "" +  hostname = "" +  omit_hostname = false +{% if azure_data_explorer is vyos_defined %} +### Azure Data Explorer ### +[[outputs.azure_data_explorer]] +  ## The URI property of the Azure Data Explorer resource on Azure +  endpoint_url = "{{ azure_data_explorer.url }}" + +  ## The Azure Data Explorer database that the metrics will be ingested into. +  ## The plugin will NOT generate this database automatically, it's expected that this database already exists before ingestion. +  database = "{{ azure_data_explorer.database }}" +  metrics_grouping_type = "{{ azure_data_explorer.group_metrics }}" + +  ## Name of the single table to store all the metrics (Only needed if metrics_grouping_type is "SingleTable"). +{%     if azure_data_explorer.table is vyos_defined and azure_data_explorer.group_metrics == 'SingleTable' %} +  table_name = "{{ azure_data_explorer.table }}" +{%     endif %} +### End Azure Data Explorer ### +{% endif %} +{% if influxdb_configured is vyos_defined %} +### InfluxDB2 ### +[[outputs.influxdb_v2]] +  urls = ["{{ url }}:{{ port }}"] +  insecure_skip_verify = true +  token = "$INFLUX_TOKEN" +  organization = "{{ authentication.organization }}" +  bucket = "{{ bucket }}" +### End InfluxDB2 ### +{% endif %} +{% if prometheus_client is vyos_defined %} +### Prometheus ### +[[outputs.prometheus_client]] +  ## Address to listen on +  listen = "{{ prometheus_client.listen_address if prometheus_client.listen_address is vyos_defined else '' }}:{{ prometheus_client.port }}" +  metric_version = {{ prometheus_client.metric_version }} +{%     if prometheus_client.authentication.username is vyos_defined and prometheus_client.authentication.password is vyos_defined  %} +  ## Use HTTP Basic Authentication +  basic_username = "{{ prometheus_client.authentication.username }}" +  basic_password = "{{ prometheus_client.authentication.password }}" +{%     endif %} +{%     if prometheus_client.allow_from is vyos_defined %} +  ip_range = {{ prometheus_client.allow_from }} +{%     endif %} +### End Prometheus ### +{% endif %} +{% if splunk is vyos_defined %} +### Splunk ### +[[outputs.http]] +  ## URL is the address to send metrics to +  url = "{{ splunk.url }}" +  ## Timeout for HTTP message +  # timeout = "5s" +  ## Use TLS but skip chain & host verification +{%     if splunk.authentication.insecure is vyos_defined %} +  insecure_skip_verify = true +{%     endif %} +  ## Data format to output +  data_format = "splunkmetric" +  ## Provides time, index, source overrides for the HEC +  splunkmetric_hec_routing = true +  ## Additional HTTP headers +   [outputs.http.headers] +   # Should be set manually to "application/json" for json data_format +     Content-Type = "application/json" +     Authorization = "Splunk {{ splunk.authentication.token }}" +     X-Splunk-Request-Channel = "{{ splunk.authentication.token }}" +### End Splunk ### +{% endif %} +[[inputs.cpu]] +    percpu = true +    totalcpu = true +    collect_cpu_time = false +    report_active = false +[[inputs.disk]] +    ignore_fs = ["devtmpfs", "devfs"] +[[inputs.diskio]] +[[inputs.mem]] +[[inputs.net]] +[[inputs.system]] +[[inputs.netstat]] +[[inputs.processes]] +[[inputs.kernel]] +[[inputs.interrupts]] +[[inputs.linux_sysctl_fs]] +[[inputs.systemd_units]] +[[inputs.conntrack]] +  files = ["ip_conntrack_count","ip_conntrack_max","nf_conntrack_count","nf_conntrack_max"] +  dirs = ["/proc/sys/net/ipv4/netfilter","/proc/sys/net/netfilter"] +[[inputs.ethtool]] +  interface_include = {{ interfaces_ethernet }} +[[inputs.ntpq]] +  dns_lookup = true +[[inputs.internal]] +[[inputs.nstat]] +[[inputs.syslog]] +  server = "unixgram:///run/telegraf/telegraf_syslog.sock" +  best_effort = true +  syslog_standard = "RFC3164" +{% if influxdb_configured is vyos_defined %} +[[inputs.exec]] +  commands = [ +    "{{ custom_scripts_dir }}/show_firewall_input_filter.py", +    "{{ custom_scripts_dir }}/show_interfaces_input_filter.py", +    "{{ custom_scripts_dir }}/vyos_services_input_filter.py" +  ] +  timeout = "10s" +  data_format = "influx" +{% endif %} diff --git a/data/templates/monitoring/telegraf.tmpl b/data/templates/monitoring/telegraf.tmpl deleted file mode 100644 index cf33eec4e..000000000 --- a/data/templates/monitoring/telegraf.tmpl +++ /dev/null @@ -1,60 +0,0 @@ -# Generated by /usr/libexec/vyos/conf_mode/service_monitoring_telegraf.py - -[agent] -  interval = "15s" -  round_interval = true -  metric_batch_size = 1000 -  metric_buffer_limit = 10000 -  collection_jitter = "5s" -  flush_interval = "15s" -  flush_jitter = "0s" -  precision = "" -  debug = false -  quiet = false -  logfile = "" -  hostname = "" -  omit_hostname = false -[[outputs.influxdb_v2]] -  urls = ["{{ url }}:{{ port }}"] -  insecure_skip_verify = true -  token = "$INFLUX_TOKEN" -  organization = "{{ authentication.organization }}" -  bucket = "{{ bucket }}" -[[inputs.cpu]] -    percpu = true -    totalcpu = true -    collect_cpu_time = false -    report_active = false -[[inputs.disk]] -    ignore_fs = ["devtmpfs", "devfs"] -[[inputs.diskio]] -[[inputs.mem]] -[[inputs.net]] -[[inputs.system]] -[[inputs.netstat]] -[[inputs.processes]] -[[inputs.kernel]] -[[inputs.interrupts]] -[[inputs.linux_sysctl_fs]] -[[inputs.systemd_units]] -[[inputs.conntrack]] -  files = ["ip_conntrack_count","ip_conntrack_max","nf_conntrack_count","nf_conntrack_max"] -  dirs = ["/proc/sys/net/ipv4/netfilter","/proc/sys/net/netfilter"] -[[inputs.ethtool]] -  interface_include = {{ interfaces_ethernet }} -[[inputs.ntpq]] -  dns_lookup = true -[[inputs.internal]] -[[inputs.nstat]] -[[inputs.syslog]] -  server = "unixgram:///run/telegraf/telegraf_syslog.sock" -  best_effort = true -  syslog_standard = "RFC3164" -[[inputs.exec]] -  commands = [ -    "{{ custom_scripts_dir }}/show_firewall_input_filter.py", -    "{{ custom_scripts_dir }}/show_interfaces_input_filter.py", -    "{{ custom_scripts_dir }}/vyos_services_input_filter.py" -  ] -  timeout = "10s" -  data_format = "influx" diff --git a/data/templates/ndppd/ndppd.conf.j2 b/data/templates/ndppd/ndppd.conf.j2 new file mode 100644 index 000000000..120fa0a64 --- /dev/null +++ b/data/templates/ndppd/ndppd.conf.j2 @@ -0,0 +1,44 @@ +######################################################## +# +# autogenerated by nat66.py +# +#   The configuration file must define one upstream +#   interface. +# +#   For some services, such as nat66, because it runs +#    stateless, it needs to rely on NDP Proxy to respond +#   to NDP requests. +# +#   When using nat66 source rules, NDP Proxy needs +#   to be enabled +# +######################################################## + +{% set global = namespace(ndppd_interfaces = [],ndppd_prefixs = []) %} +{% if source.rule is vyos_defined %} +{%     for rule, config in source.rule.items() if config.disable is not defined %} +{%         if config.outbound_interface is vyos_defined %} +{%             if config.outbound_interface not in global.ndppd_interfaces %} +{%                 set global.ndppd_interfaces = global.ndppd_interfaces + [config.outbound_interface] %} +{%             endif   %} +{%             if config.translation.address is vyos_defined and config.translation.address | is_ip_network %} +{%                 set global.ndppd_prefixs = global.ndppd_prefixs + [{'interface':config.outbound_interface,'rule':config.translation.address}] %} +{%             endif %} +{%         endif %} +{%     endfor %} +{% endif %} + +{% for interface in global.ndppd_interfaces %} +proxy {{ interface }} { +    router yes +    timeout 500 +    ttl 30000 +{%     for map in global.ndppd_prefixs %} +{%         if map.interface == interface %} +    rule {{ map.rule }} { +        static +    } +{%         endif  %} +{%     endfor   %} +} +{% endfor %} diff --git a/data/templates/ndppd/ndppd.conf.tmpl b/data/templates/ndppd/ndppd.conf.tmpl deleted file mode 100644 index c41392cc7..000000000 --- a/data/templates/ndppd/ndppd.conf.tmpl +++ /dev/null @@ -1,44 +0,0 @@ -######################################################## -# -# autogenerated by nat66.py -# -#   The configuration file must define one upstream -#   interface. -# -#   For some services, such as nat66, because it runs -#    stateless, it needs to rely on NDP Proxy to respond -#   to NDP requests. -# -#   When using nat66 source rules, NDP Proxy needs -#   to be enabled -# -######################################################## - -{% set global = namespace(ndppd_interfaces = [],ndppd_prefixs = []) %} -{% if source.rule is vyos_defined %} -{%   for rule, config in source.rule.items() if config.disable is not defined %} -{%     if config.outbound_interface is vyos_defined %} -{%         if config.outbound_interface not in global.ndppd_interfaces %} -{%             set global.ndppd_interfaces = global.ndppd_interfaces + [config.outbound_interface] %} -{%         endif   %} -{%         if config.translation.address is vyos_defined and config.translation.address | is_ip_network %} -{%             set global.ndppd_prefixs = global.ndppd_prefixs + [{'interface':config.outbound_interface,'rule':config.translation.address}] %} -{%         endif    %} -{%     endif    %} -{%   endfor %} -{% endif %} - -{% for interface in global.ndppd_interfaces %} -proxy {{ interface }} { -    router yes -    timeout 500 -    ttl 30000 -{%  for map in global.ndppd_prefixs %} -{%     if map.interface == interface %} -    rule {{ map.rule }} { -        static -    } -{%     endif  %} -{%  endfor   %} -} -{% endfor %} diff --git a/data/templates/nhrp/opennhrp.conf.j2 b/data/templates/nhrp/opennhrp.conf.j2 new file mode 100644 index 000000000..c040a8f14 --- /dev/null +++ b/data/templates/nhrp/opennhrp.conf.j2 @@ -0,0 +1,42 @@ +{# j2lint: disable=jinja-variable-format #} +# Created by VyOS - manual changes will be overwritten + +{% if tunnel is vyos_defined %} +{%     for name, tunnel_conf in tunnel.items() %} +{%         set type = 'spoke' if tunnel_conf.map is vyos_defined or tunnel_conf.dynamic_map is vyos_defined else 'hub' %} +{%         set profile_name = profile_map[name] if profile_map is vyos_defined and name in profile_map else '' %} +interface {{ name }} #{{ type }} {{ profile_name }} +{%         if tunnel_conf.map is vyos_defined %} +{%             for map, map_conf in tunnel_conf.map.items() %} +{%                 set cisco = ' cisco' if map_conf.cisco is vyos_defined else '' %} +{%                 set register = ' register' if map_conf.register is vyos_defined else '' %} +    map {{ map }} {{ map_conf.nbma_address }}{{ register }}{{ cisco }} +{%             endfor %} +{%         endif %} +{%         if tunnel_conf.dynamic_map is vyos_defined %} +{%             for map, map_conf in tunnel_conf.dynamic_map.items() %} +    dynamic-map {{ map }} {{ map_conf.nbma_domain_name }} +{%             endfor %} +{%         endif %} +{%         if tunnel_conf.cisco_authentication is vyos_defined %} +    cisco-authentication {{ tunnel_conf.cisco_authentication }} +{%         endif %} +{%         if tunnel_conf.holding_time is vyos_defined %} +    holding-time {{ tunnel_conf.holding_time }} +{%         endif %} +{%         if tunnel_conf.multicast is vyos_defined %} +    multicast {{ tunnel_conf.multicast }} +{%         endif %} +{%         for key in ['non_caching', 'redirect', 'shortcut', 'shortcut_destination'] %} +{%             if key in tunnel_conf %} +    {{ key | replace("_", "-") }} +{%             endif %} +{%         endfor %} +{%         if tunnel_conf.shortcut_target is vyos_defined %} +{%             for target, shortcut_conf in tunnel_conf.shortcut_target.items() %} +    shortcut-target {{ target }}{{ ' holding-time ' + shortcut_conf.holding_time if shortcut_conf.holding_time is vyos_defined }} +{%             endfor %} +{%         endif %} + +{%     endfor %} +{% endif %} diff --git a/data/templates/nhrp/opennhrp.conf.tmpl b/data/templates/nhrp/opennhrp.conf.tmpl deleted file mode 100644 index 721d41e49..000000000 --- a/data/templates/nhrp/opennhrp.conf.tmpl +++ /dev/null @@ -1,41 +0,0 @@ -# Created by VyOS - manual changes will be overwritten - -{% if tunnel is vyos_defined %} -{%   for name, tunnel_conf in tunnel.items() %} -{%     set type = 'spoke' if tunnel_conf.map is vyos_defined or tunnel_conf.dynamic_map is vyos_defined else 'hub' %} -{%     set profile_name = profile_map[name] if profile_map is vyos_defined and name in profile_map else '' %} -interface {{ name }} #{{ type }} {{ profile_name }} -{%     if tunnel_conf.map is vyos_defined %} -{%       for map, map_conf in tunnel_conf.map.items() %} -{%         set cisco = ' cisco' if map_conf.cisco is vyos_defined else '' %} -{%         set register = ' register' if map_conf.register is vyos_defined else '' %} -    map {{ map }} {{ map_conf.nbma_address }}{{ register }}{{ cisco }} -{%       endfor %} -{%     endif %} -{%     if tunnel_conf.dynamic_map is vyos_defined %} -{%       for map, map_conf in tunnel_conf.dynamic_map.items() %} -    dynamic-map {{ map }} {{ map_conf.nbma_domain_name }} -{%       endfor %} -{%     endif %} -{%     if tunnel_conf.cisco_authentication is vyos_defined %} -    cisco-authentication {{ tunnel_conf.cisco_authentication }} -{%     endif %} -{%     if tunnel_conf.holding_time is vyos_defined %} -    holding-time {{ tunnel_conf.holding_time }} -{%     endif %} -{%     if tunnel_conf.multicast is vyos_defined %} -    multicast {{ tunnel_conf.multicast }} -{%     endif %} -{%     for key in ['non_caching', 'redirect', 'shortcut', 'shortcut_destination'] %} -{%       if key in tunnel_conf %} -    {{ key | replace("_", "-") }} -{%       endif %} -{%     endfor %} -{%     if tunnel_conf.shortcut_target is vyos_defined %} -{%       for target, shortcut_conf in tunnel_conf.shortcut_target.items() %} -    shortcut-target {{ target }}{{ ' holding-time ' + shortcut_conf.holding_time if shortcut_conf.holding_time is vyos_defined }} -{%       endfor %} -{%     endif %} - -{%   endfor %} -{% endif %} diff --git a/data/templates/ocserv/ocserv_config.tmpl b/data/templates/ocserv/ocserv_config.j2 index 05b85a610..8418a2185 100644 --- a/data/templates/ocserv/ocserv_config.tmpl +++ b/data/templates/ocserv/ocserv_config.j2 @@ -9,13 +9,13 @@ run-as-group = daemon  {% if "radius" in authentication.mode %}  auth = "radius [config=/run/ocserv/radiusclient.conf]"  {% elif "local" in authentication.mode %} -{%   if authentication.mode.local == "password-otp" %} +{%     if authentication.mode.local == "password-otp" %}  auth = "plain[passwd=/run/ocserv/ocpasswd,otp=/run/ocserv/users.oath]" -{%   elif authentication.mode.local == "otp" %} +{%     elif authentication.mode.local == "otp" %}  auth = "plain[otp=/run/ocserv/users.oath]" -{%   else %} +{%     else %}  auth = "plain[/run/ocserv/ocpasswd]" -{%   endif %} +{%     endif %}  {% else %}  auth = "plain[/run/ocserv/ocpasswd]"  {% endif %} @@ -23,9 +23,9 @@ auth = "plain[/run/ocserv/ocpasswd]"  {% if ssl.certificate is vyos_defined %}  server-cert = /run/ocserv/cert.pem  server-key = /run/ocserv/cert.key -{% if ssl.passphrase is vyos_defined %} +{%     if ssl.passphrase is vyos_defined %}  key-pin = {{ ssl.passphrase }} -{% endif %} +{%     endif %}  {% endif %}  {% if ssl.ca_certificate is vyos_defined %} @@ -59,33 +59,33 @@ device = sslvpn  # An alternative way of specifying the network:  {% if network_settings %}  # DNS settings -{%   if network_settings.name_server is string %} +{%     if network_settings.name_server is string %}  dns = {{ network_settings.name_server }} -{%   else %} -{%     for dns in network_settings.name_server %} +{%     else %} +{%         for dns in network_settings.name_server %}  dns = {{ dns }} -{%     endfor %} -{%   endif %} +{%         endfor %} +{%     endif %}  # IPv4 network pool -{%   if network_settings.client_ip_settings %} -{%     if network_settings.client_ip_settings.subnet %} +{%     if network_settings.client_ip_settings %} +{%         if network_settings.client_ip_settings.subnet %}  ipv4-network = {{ network_settings.client_ip_settings.subnet }} +{%         endif %}  {%     endif %} -{%   endif %}  # IPv6 network pool -{%   if network_settings.client_ipv6_pool %} -{%     if network_settings.client_ipv6_pool.prefix %} +{%     if network_settings.client_ipv6_pool %} +{%         if network_settings.client_ipv6_pool.prefix %}  ipv6-network = {{ network_settings.client_ipv6_pool.prefix }}  ipv6-subnet-prefix = {{ network_settings.client_ipv6_pool.mask }} +{%         endif %}  {%     endif %} -{%   endif %}  {% endif %}  {% if network_settings.push_route is string %}  route = {{ network_settings.push_route }}  {% else %} -{%   for route in network_settings.push_route %} +{%     for route in network_settings.push_route %}  route = {{ route }} -{%   endfor %} +{%     endfor %}  {% endif %} diff --git a/data/templates/ocserv/ocserv_otp_usr.tmpl b/data/templates/ocserv/ocserv_otp_usr.j2 index 18de5fec6..b2511ed94 100644 --- a/data/templates/ocserv/ocserv_otp_usr.tmpl +++ b/data/templates/ocserv/ocserv_otp_usr.j2 @@ -1,8 +1,8 @@  #<token_type> <username> <pin> <secret_hex_key> <counter> <lastpass> <time>  {% if username is vyos_defined %} -{%   for user, user_config in username.items() %} -{%     if user_config.disable is not vyos_defined and user_config.otp is vyos_defined %} +{%     for user, user_config in username.items() %} +{%         if user_config.disable is not vyos_defined and user_config.otp is vyos_defined %}  {{ user_config.otp.token_tmpl }} {{ user }} {{ user_config.otp.pin | default("-", true) }} {{ user_config.otp.key }} -{%     endif %} -{%   endfor %} +{%         endif %} +{%     endfor %}  {% endif %} diff --git a/data/templates/ocserv/ocserv_passwd.tmpl b/data/templates/ocserv/ocserv_passwd.j2 index 30c79d66a..30c79d66a 100644 --- a/data/templates/ocserv/ocserv_passwd.tmpl +++ b/data/templates/ocserv/ocserv_passwd.j2 diff --git a/data/templates/ocserv/radius_conf.tmpl b/data/templates/ocserv/radius_conf.j2 index 1712d83ef..b6612fee5 100644 --- a/data/templates/ocserv/radius_conf.tmpl +++ b/data/templates/ocserv/radius_conf.j2 @@ -1,13 +1,13 @@  ### generated by vpn_openconnect.py ###  nas-identifier VyOS  {% for srv in server %} -{%   if not "disable" in server[srv] %} -{%     if "port" in server[srv] %} -authserver {{ srv }}:{{server[srv]["port"]}} -{%     else %} +{%     if not "disable" in server[srv] %} +{%         if "port" in server[srv] %} +authserver {{ srv }}:{{ server[srv]["port"] }} +{%         else %}  authserver {{ srv }} +{%         endif %}  {%     endif %} -{%   endif %}  {% endfor %}  radius_timeout {{ timeout }}  {% if source_address %} @@ -15,7 +15,7 @@ bindaddr {{ source_address }}  {% else %}  bindaddr *  {% endif %} -servers /run/ocserv/radius_servers   +servers /run/ocserv/radius_servers  dictionary /etc/radcli/dictionary  default_realm  radius_retries 3 diff --git a/data/templates/ocserv/radius_servers.j2 b/data/templates/ocserv/radius_servers.j2 new file mode 100644 index 000000000..302e91600 --- /dev/null +++ b/data/templates/ocserv/radius_servers.j2 @@ -0,0 +1,7 @@ +### generated by vpn_openconnect.py ### +# server   key +{% for srv in server %} +{%     if not "disable" in server[srv] %} +{{ srv }}   {{ server[srv].key }} +{%     endif %} +{% endfor %} diff --git a/data/templates/ocserv/radius_servers.tmpl b/data/templates/ocserv/radius_servers.tmpl deleted file mode 100644 index 7bacac992..000000000 --- a/data/templates/ocserv/radius_servers.tmpl +++ /dev/null @@ -1,7 +0,0 @@ -### generated by vpn_openconnect.py ### -# server	key -{% for srv in server %} -{%   if not "disable" in server[srv] %} -{{ srv }}	{{ server[srv].key }} -{%   endif %} -{% endfor %} diff --git a/data/templates/pmacct/override.conf.tmpl b/data/templates/pmacct/override.conf.j2 index 213569ddc..213569ddc 100644 --- a/data/templates/pmacct/override.conf.tmpl +++ b/data/templates/pmacct/override.conf.j2 diff --git a/data/templates/pmacct/uacctd.conf.j2 b/data/templates/pmacct/uacctd.conf.j2 new file mode 100644 index 000000000..a5016691f --- /dev/null +++ b/data/templates/pmacct/uacctd.conf.j2 @@ -0,0 +1,80 @@ +# Genereated from VyOS configuration +daemonize: true +promisc: false +pidfile: /run/pmacct/uacctd.pid +uacctd_group: 2 +uacctd_nl_size: 2097152 +snaplen: {{ packet_length }} +aggregate: in_iface{{ ',out_iface' if enable_egress is vyos_defined }},src_mac,dst_mac,vlan,src_host,dst_host,src_port,dst_port,proto,tos,flows +{% set pipe_size = buffer_size | int *1024 *1024 %} +plugin_pipe_size: {{ pipe_size }} +{# We need an integer division (//) without any remainder or fraction #} +plugin_buffer_size: {{ pipe_size // 1000 }} +{% if syslog_facility is vyos_defined %} +syslog: {{ syslog_facility }} +{% endif %} +{% if disable_imt is not defined %} +imt_path: /tmp/uacctd.pipe +imt_mem_pools_number: 169 +{% endif %} + +{% set plugin = [] %} +{% if netflow.server is vyos_defined %} +{%     for server in netflow.server %} +{%         set nf_server_key = 'nf_' ~ server | replace(':', '.') %} +{%         set _ = plugin.append('nfprobe['~ nf_server_key ~ ']') %} +{%     endfor %} +{% endif %} +{% if sflow.server is vyos_defined %} +{%     for server in sflow.server %} +{%         set sf_server_key = 'sf_' ~ server | replace(':', '.') %} +{%         set _ = plugin.append('sfprobe[' ~ sf_server_key ~ ']') %} +{%     endfor %} +{% endif %} +{% if disable_imt is not defined %} +{%     set _ = plugin.append('memory') %} +{% endif %} +plugins: {{ plugin | join(',') }} + +{% if netflow.server is vyos_defined %} +# NetFlow servers +{%     for server, server_config in netflow.server.items() %} +{#         # prevent pmacct syntax error when using IPv6 flow collectors #} +{%         set nf_server_key = 'nf_' ~ server | replace(':', '.') %} +nfprobe_receiver[{{ nf_server_key }}]: {{ server | bracketize_ipv6 }}:{{ server_config.port }} +nfprobe_version[{{ nf_server_key }}]: {{ netflow.version }} +{%         if netflow.engine_id is vyos_defined %} +nfprobe_engine[{{ nf_server_key }}]: {{ netflow.engine_id }} +{%         endif %} +{%         if netflow.max_flows is vyos_defined %} +nfprobe_maxflows[{{ nf_server_key }}]: {{ netflow.max_flows }} +{%         endif %} +{%         if netflow.sampling_rate is vyos_defined %} +sampling_rate[{{ nf_server_key }}]: {{ netflow.sampling_rate }} +{%         endif %} +{%         if netflow.source_address is vyos_defined %} +nfprobe_source_ip[{{ nf_server_key }}]: {{ netflow.source_address }} +{%         endif %} +{%         if netflow.timeout is vyos_defined %} +nfprobe_timeouts[{{ nf_server_key }}]: expint={{ netflow.timeout.expiry_interval }}:general={{ netflow.timeout.flow_generic }}:icmp={{ netflow.timeout.icmp }}:maxlife={{ netflow.timeout.max_active_life }}:tcp.fin={{ netflow.timeout.tcp_fin }}:tcp={{ netflow.timeout.tcp_generic }}:tcp.rst={{ netflow.timeout.tcp_rst }}:udp={{ netflow.timeout.udp }} +{%         endif %} + +{%     endfor %} +{% endif %} + +{% if sflow.server is vyos_defined %} +# sFlow servers +{%     for server, server_config in sflow.server.items() %} +{#         # prevent pmacct syntax error when using IPv6 flow collectors #} +{%         set sf_server_key = 'sf_' ~ server | replace(':', '.') %} +sfprobe_receiver[{{ sf_server_key }}]: {{ server | bracketize_ipv6 }}:{{ server_config.port }} +sfprobe_agentip[{{ sf_server_key }}]: {{ sflow.agent_address }} +{%         if sflow.sampling_rate is vyos_defined %} +sampling_rate[{{ sf_server_key }}]: {{ sflow.sampling_rate }} +{%         endif %} +{%         if sflow.source_address is vyos_defined %} +sfprobe_source_ip[{{ sf_server_key }}]: {{ sflow.source_address }} +{%         endif %} + +{%     endfor %} +{% endif %} diff --git a/data/templates/pmacct/uacctd.conf.tmpl b/data/templates/pmacct/uacctd.conf.tmpl deleted file mode 100644 index 7e4f80e95..000000000 --- a/data/templates/pmacct/uacctd.conf.tmpl +++ /dev/null @@ -1,74 +0,0 @@ -# Genereated from VyOS configuration -daemonize: true -promisc: false -pidfile: /run/pmacct/uacctd.pid -uacctd_group: 2 -uacctd_nl_size: 2097152 -snaplen: {{ packet_length }} -aggregate: in_iface{{ ',out_iface' if enable_egress is vyos_defined }},src_mac,dst_mac,vlan,src_host,dst_host,src_port,dst_port,proto,tos,flows -{% set pipe_size = buffer_size | int *1024 *1024 %} -plugin_pipe_size: {{ pipe_size }} -{# We need an integer division (//) without any remainder or fraction #} -plugin_buffer_size: {{ pipe_size // 1000 }} -{% if syslog_facility is vyos_defined %} -syslog: {{ syslog_facility }} -{% endif %} -{% if disable_imt is not defined %} -imt_path: /tmp/uacctd.pipe -imt_mem_pools_number: 169 -{% endif %} - -{% set plugin = [] %} -{% if netflow.server is vyos_defined %} -{%   for server in netflow.server %} -{%     set _ = plugin.append('nfprobe[nf_' ~ server ~ ']') %} -{%   endfor %} -{% endif %} -{% if sflow.server is vyos_defined %} -{%   for server in sflow.server %} -{%     set _ = plugin.append('sfprobe[sf_' ~ server ~ ']') %} -{%   endfor %} -{% endif %} -{% if disable_imt is not defined %} -{%     set _ = plugin.append('memory') %} -{% endif %} -plugins: {{ plugin | join(',') }} - -{% if netflow.server is vyos_defined %} -# NetFlow servers -{%   for server, server_config in netflow.server.items() %} -nfprobe_receiver[nf_{{ server }}]: {{ server }}:{{ server_config.port }} -nfprobe_version[nf_{{ server }}]: {{ netflow.version }} -{%     if netflow.engine_id is vyos_defined %} -nfprobe_engine[nf_{{ server }}]: {{ netflow.engine_id }} -{%     endif %} -{%     if netflow.max_flows is vyos_defined %} -nfprobe_maxflows[nf_{{ server }}]: {{ netflow.max_flows }} -{%     endif %} -{%     if netflow.sampling_rate is vyos_defined %} -sampling_rate[nf_{{ server }}]: {{ netflow.sampling_rate }} -{%     endif %} -{%     if netflow.source_address is vyos_defined %} -nfprobe_source_ip[nf_{{ server }}]: {{ netflow.source_address }} -{%     endif %} -{%     if netflow.timeout is vyos_defined %} -nfprobe_timeouts[nf_{{ server }}]: expint={{ netflow.timeout.expiry_interval }}:general={{ netflow.timeout.flow_generic }}:icmp={{ netflow.timeout.icmp }}:maxlife={{ netflow.timeout.max_active_life }}:tcp.fin={{ netflow.timeout.tcp_fin }}:tcp={{ netflow.timeout.tcp_generic }}:tcp.rst={{ netflow.timeout.tcp_rst }}:udp={{ netflow.timeout.udp }} -{%     endif %} - -{%   endfor %} -{% endif %} - -{% if sflow.server is vyos_defined %} -# sFlow servers -{%   for server, server_config in sflow.server.items() %} -sfprobe_receiver[sf_{{ server }}]: {{ server }}:{{ server_config.port }} -sfprobe_agentip[sf_{{ server }}]: {{ sflow.agent_address }} -{%     if sflow.sampling_rate is vyos_defined %} -sampling_rate[sf_{{ server }}]: {{ sflow.sampling_rate }} -{%     endif %} -{%     if sflow.source_address is vyos_defined %} -sfprobe_source_ip[sf_{{ server }}]: {{ sflow.source_address }} -{%     endif %} - -{%   endfor %} -{% endif %} diff --git a/data/templates/pppoe/ip-down.script.tmpl b/data/templates/pppoe/ip-down.script.tmpl deleted file mode 100644 index 0be7b03c8..000000000 --- a/data/templates/pppoe/ip-down.script.tmpl +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/sh - -# As PPPoE is an "on demand" interface we need to re-configure it when it -# becomes up -if [ "$6" != "{{ ifname }}" ]; then -    exit -fi - -# add some info to syslog -DIALER_PID=$(cat /var/run/{{ ifname }}.pid) -logger -t pppd[$DIALER_PID] "executing $0" - -{% if connect_on_demand is not defined %} -# See https://phabricator.vyos.net/T2248. Determine if we are enslaved to a -# VRF, this is needed to properly insert the default route. -VRF_NAME="" -if [ -d /sys/class/net/{{ ifname }}/upper_* ]; then -    # Determine upper (VRF) interface -    VRF=$(basename $(ls -d /sys/class/net/{{ ifname }}/upper_*)) -    # Remove upper_ prefix from result string -    VRF=${VRF#"upper_"} -    # Populate variable to run in VR context -    VRF_NAME="vrf ${VRF_NAME}" -fi - -{%   if default_route != 'none' %} -# Always delete default route when interface goes down if we installed it -vtysh -c "conf t" ${VRF_NAME} -c "no ip route 0.0.0.0/0 {{ ifname }} ${VRF_NAME}" -{%      if ipv6.address.autoconf is vyos_defined %} -vtysh -c "conf t" ${VRF_NAME} -c "no ipv6 route ::/0 {{ ifname }} ${VRF_NAME}" -{%      endif %} -{%   endif %} -{% endif %} - -{% if dhcpv6_options.pd is vyos_defined %} -# Stop wide dhcpv6 client -systemctl stop dhcp6c@{{ ifname }}.service -{% endif %} diff --git a/data/templates/pppoe/ip-pre-up.script.tmpl b/data/templates/pppoe/ip-pre-up.script.tmpl deleted file mode 100644 index a54e4e9bd..000000000 --- a/data/templates/pppoe/ip-pre-up.script.tmpl +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/sh - -# As PPPoE is an "on demand" interface we need to re-configure it when it -# becomes up -if [ "$6" != "{{ ifname }}" ]; then -    exit -fi - -# add some info to syslog -DIALER_PID=$(cat /var/run/{{ ifname }}.pid) -logger -t pppd[$DIALER_PID] "executing $0" - -echo "{{ description }}" > /sys/class/net/{{ ifname }}/ifalias - -{% if vrf %} -logger -t pppd[$DIALER_PID] "configuring dialer interface $6 for VRF {{ vrf }}" -ip link set dev {{ ifname }} master {{ vrf }} -{% endif %} diff --git a/data/templates/pppoe/ip-up.script.tmpl b/data/templates/pppoe/ip-up.script.tmpl deleted file mode 100644 index 302756960..000000000 --- a/data/templates/pppoe/ip-up.script.tmpl +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/sh - -# As PPPoE is an "on demand" interface we need to re-configure it when it -# becomes up -if [ "$6" != "{{ ifname }}" ]; then -    exit -fi - -{% if connect_on_demand is not defined %} -# add some info to syslog -DIALER_PID=$(cat /var/run/{{ ifname }}.pid) -logger -t pppd[$DIALER_PID] "executing $0" - -{%   if default_route != 'none' %} -# See https://phabricator.vyos.net/T2248 & T2220. Determine if we are enslaved -# to a VRF, this is needed to properly insert the default route. - -SED_OPT="^ip route" -VRF_NAME="" -if [ -d /sys/class/net/{{ ifname }}/upper_* ]; then -    # Determine upper (VRF) interface -    VRF=$(basename $(ls -d /sys/class/net/{{ ifname }}/upper_*)) -    # Remove upper_ prefix from result string -    VRF=${VRF#"upper_"} -    # generate new SED command -    SED_OPT="vrf ${VRF}" -    # generate vtysh option -    VRF_NAME="vrf ${VRF}" -fi - -{%     if default_route == 'auto' %} -# Only insert a new default route if there is no default route configured -routes=$(vtysh -c "show running-config" | sed -n "/${SED_OPT}/,/!/p" | grep 0.0.0.0/0 | wc -l) -if [ "$routes" -ne 0 ]; then -    exit 1 -fi - -{%     elif default_route == 'force' %} -# Retrieve current static default routes and remove it from the routing table -vtysh -c "show running-config" | sed -n "/${SED_OPT}/,/!/p" | grep 0.0.0.0/0 | while read route ; do -    vtysh -c "conf t" ${VTY_OPT} -c "no ${route} ${VRF_NAME}" -done -{%     endif %} - -# Add default route to default or VRF routing table -vtysh -c "conf t" ${VTY_OPT} -c "ip route 0.0.0.0/0 {{ ifname }} ${VRF_NAME}" -logger -t pppd[$DIALER_PID] "added default route via {{ ifname }} ${VRF_NAME}" -{%   endif %} -{% endif %} diff --git a/data/templates/pppoe/ipv6-up.script.tmpl b/data/templates/pppoe/ipv6-up.script.tmpl deleted file mode 100644 index da73cb4d5..000000000 --- a/data/templates/pppoe/ipv6-up.script.tmpl +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/sh - -# As PPPoE is an "on demand" interface we need to re-configure it when it -# becomes up - -if [ "$6" != "{{ ifname }}" ]; then -    exit -fi - - -{% if default_route != 'none' %} -# See https://phabricator.vyos.net/T2248 & T2220. Determine if we are enslaved -# to a VRF, this is needed to properly insert the default route. - -SED_OPT="^ipv6 route" -VRF_NAME="" -if [ -d /sys/class/net/{{ ifname }}/upper_* ]; then -    # Determine upper (VRF) interface -    VRF=$(basename $(ls -d /sys/class/net/{{ ifname }}/upper_*)) -    # Remove upper_ prefix from result string -    VRF=${VRF#"upper_"} -    # generate new SED command -    SED_OPT="vrf ${VRF}" -    # generate vtysh option -    VRF_NAME="vrf ${VRF}" -fi - -{%   if default_route == 'auto' %} -# Only insert a new default route if there is no default route configured -routes=$(vtysh -c "show running-config" | sed -n "/${SED_OPT}/,/!/p" | grep ::/0 | wc -l) -if [ "$routes" -ne 0 ]; then -    exit 1 -fi - -{%   elif default_route == 'force' %} -# Retrieve current static default routes and remove it from the routing table -vtysh -c "show running-config" | sed -n "/${SED_OPT}/,/!/p" | grep ::/0 | while read route ; do -    vtysh -c "conf t" ${VTY_OPT} -c "no ${route} ${VRF_NAME}" -done -{%   endif %} - -# Add default route to default or VRF routing table -vtysh -c "conf t" ${VTY_OPT} -c "ipv6 route ::/0 {{ ifname }} ${VRF_NAME}" -logger -t pppd[$DIALER_PID] "added default route via {{ ifname }} ${VRF_NAME}" -{% endif %} - diff --git a/data/templates/pppoe/peer.tmpl b/data/templates/pppoe/peer.j2 index d6d63debf..6221abb9b 100644 --- a/data/templates/pppoe/peer.tmpl +++ b/data/templates/pppoe/peer.j2 @@ -67,14 +67,14 @@ demand  # See T2249. PPP default route options should only be set when in on-demand  # mode. As soon as we are not in on-demand mode the default-route handling is  # passed to the ip-up.d/ip-down.s scripts which is required for VRF support. -{%   if 'auto' in default_route %} +{%     if 'auto' in default_route %}  defaultroute  {{ 'defaultroute6' if ipv6 is vyos_defined }} -{%   elif 'force' in default_route %} +{%     elif 'force' in default_route %}  defaultroute  replacedefaultroute  {{ 'defaultroute6' if ipv6 is vyos_defined }} -{%   endif %} +{%     endif %}  {% else %}  nodefaultroute  noreplacedefaultroute diff --git a/data/templates/router-advert/radvd.conf.tmpl b/data/templates/router-advert/radvd.conf.j2 index b40ba1ee0..6902dc05a 100644 --- a/data/templates/router-advert/radvd.conf.tmpl +++ b/data/templates/router-advert/radvd.conf.j2 @@ -1,66 +1,66 @@  ### Autogenerated by service_router-advert.py ###  {% if interface is vyos_defined %} -{%   for iface, iface_config in interface.items() %} +{%     for iface, iface_config in interface.items() %}  interface {{ iface }} {      IgnoreIfMissing on; -{%     if iface_config.default_preference is vyos_defined %} +{%         if iface_config.default_preference is vyos_defined %}      AdvDefaultPreference {{ iface_config.default_preference }}; -{%     endif %} -{%     if iface_config.managed_flag is vyos_defined %} +{%         endif %} +{%         if iface_config.managed_flag is vyos_defined %}      AdvManagedFlag {{ 'on' if iface_config.managed_flag is vyos_defined else 'off' }}; -{%     endif %} -{%     if iface_config.interval.max is vyos_defined %} +{%         endif %} +{%         if iface_config.interval.max is vyos_defined %}      MaxRtrAdvInterval {{ iface_config.interval.max }}; -{%     endif %} -{%     if iface_config.interval.min is vyos_defined %} +{%         endif %} +{%         if iface_config.interval.min is vyos_defined %}      MinRtrAdvInterval {{ iface_config.interval.min }}; -{%   endif %} -{%     if iface_config.reachable_time is vyos_defined %} +{%         endif %} +{%         if iface_config.reachable_time is vyos_defined %}      AdvReachableTime {{ iface_config.reachable_time }}; -{%   endif %} +{%         endif %}      AdvIntervalOpt {{ 'off' if iface_config.no_send_advert is vyos_defined else 'on' }};      AdvSendAdvert {{ 'off' if iface_config.no_send_advert is vyos_defined else 'on' }}; -{%     if iface_config.default_lifetime is vyos_defined %} +{%         if iface_config.default_lifetime is vyos_defined %}      AdvDefaultLifetime {{ iface_config.default_lifetime }}; -{%     endif %} -{%     if iface_config.link_mtu is vyos_defined %} +{%         endif %} +{%         if iface_config.link_mtu is vyos_defined %}      AdvLinkMTU {{ iface_config.link_mtu }}; -{%     endif %} +{%         endif %}      AdvOtherConfigFlag {{ 'on' if iface_config.other_config_flag is vyos_defined else 'off' }};      AdvRetransTimer {{ iface_config.retrans_timer }};      AdvCurHopLimit {{ iface_config.hop_limit }}; -{%     if iface_config.route is vyos_defined %} -{%       for route, route_options in iface_config.route.items() %} +{%         if iface_config.route is vyos_defined %} +{%             for route, route_options in iface_config.route.items() %}      route {{ route }} { -{%         if route_options.valid_lifetime is vyos_defined %} +{%                 if route_options.valid_lifetime is vyos_defined %}          AdvRouteLifetime {{ route_options.valid_lifetime }}; -{%         endif %} -{%         if route_options.route_preference is vyos_defined %} +{%                 endif %} +{%                 if route_options.route_preference is vyos_defined %}          AdvRoutePreference {{ route_options.route_preference }}; -{%         endif %} +{%                 endif %}          RemoveRoute {{ 'off' if route_options.no_remove_route is vyos_defined else 'on' }};      }; -{%       endfor %} -{%     endif %} -{%     if iface_config.prefix is vyos_defined %} -{%       for prefix, prefix_options in iface_config.prefix.items() %} +{%             endfor %} +{%         endif %} +{%         if iface_config.prefix is vyos_defined %} +{%             for prefix, prefix_options in iface_config.prefix.items() %}      prefix {{ prefix }} {          AdvAutonomous {{ 'off' if prefix_options.no_autonomous_flag is vyos_defined else 'on' }};          AdvValidLifetime {{ prefix_options.valid_lifetime }};          AdvOnLink {{ 'off' if prefix_options.no_on_link_flag is vyos_defined else 'on' }};          AdvPreferredLifetime {{ prefix_options.preferred_lifetime }};      }; -{%       endfor %} -{%     endif %} -{%     if iface_config.name_server is vyos_defined %} +{%             endfor %} +{%         endif %} +{%         if iface_config.name_server is vyos_defined %}      RDNSS {{ iface_config.name_server | join(" ") }} {      }; -{%     endif %} -{%     if iface_config.dnssl is vyos_defined %} +{%         endif %} +{%         if iface_config.dnssl is vyos_defined %}      DNSSL {{ iface_config.dnssl | join(" ") }} {      }; -{%     endif %} +{%         endif %}  }; -{%   endfor %} +{%     endfor %}  {% endif %} diff --git a/data/templates/snmp/etc.snmp.conf.tmpl b/data/templates/snmp/etc.snmp.conf.j2 index 8012cf6bb..8012cf6bb 100644 --- a/data/templates/snmp/etc.snmp.conf.tmpl +++ b/data/templates/snmp/etc.snmp.conf.j2 diff --git a/data/templates/snmp/etc.snmpd.conf.j2 b/data/templates/snmp/etc.snmpd.conf.j2 new file mode 100644 index 000000000..d7dc0ba5d --- /dev/null +++ b/data/templates/snmp/etc.snmpd.conf.j2 @@ -0,0 +1,182 @@ +### 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 + +# Remove all old ifTable entries with the same ifName as newly appeared +# interface (with different ifIndex) - this is the case on e.g. ppp interfaces +interface_replace_old yes + +######################## +# configurable section # +######################## + +# Default system description is VyOS version +sysDescr VyOS {{ version }} + +{% if description is vyos_defined %} +# Description +SysDescr {{ description }} +{% endif %} + +# Listen +{% set options = [] %} +{% if listen_address is vyos_defined %} +{%     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') %} +{%     set _ = options.append(protocol ~ '6:161') %} +{% endif %} +agentaddress unix:/run/snmpd.socket{{ ',' ~ options | join(',') if options is vyos_defined }} + +# SNMP communities +{% if community is vyos_defined %} +{%     for comm, comm_config in community.items() %} +{%         if comm_config.client is vyos_defined %} +{%             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 vyos_defined %} +{%             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 vyos_defined and comm_config.network is not vyos_defined %} +{{ comm_config.authorization }}community {{ comm }} +{%         endif %} +{%     endfor %} +{% endif %} + +{% if contact is vyos_defined %} +# system contact information +SysContact {{ contact }} +{% endif %} + +{% if location is vyos_defined %} +# system location information +SysLocation {{ location }} +{% endif %} + +{% if smux_peer is vyos_defined %} +# additional smux peers +{%     for peer in smux_peer %} +smuxpeer {{ peer }} +{%     endfor %} +{% endif %} + +{% if trap_target is vyos_defined %} +# if there is a problem - tell someone! +{%     for trap, trap_config in trap_target.items() %} +trap2sink {{ trap }}:{{ trap_config.port }} {{ trap_config.community }} +{%     endfor %} +{% endif %} + +{% if v3 is vyos_defined %} +# +# SNMPv3 stuff goes here +# +{%     if v3.view is vyos_defined %} +# views +{%         for view, view_config in v3.view.items() %} +{%             if view_config.oid is vyos_defined %} +{%                 for oid in view_config.oid %} +view {{ view }} included .{{ oid }} +{%                 endfor %} +{%             endif %} +{%         endfor %} +{%     endif %} + +# access +{%     if v3.group is vyos_defined %} +#             context sec.model sec.level match  read    write  notif +{%         for group, group_config in v3.group.items() %} +access {{ group }} "" usm {{ group_config.seclevel }} exact {{ group_config.view }} {{ 'none' if group_config.mode == 'ro' else group_config.view }} none +{%         endfor %} +{%     endif %} + +# trap-target +{%     if v3.trap_target is vyos_defined %} +{%         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 vyos_defined %} +{%                 set options = options ~ ' -e "' ~ v3.engineid ~ '"' %} +{%             endif %} +{%             if trap_config.user is vyos_defined %} +{%                 set options = options ~ ' -u ' ~ trap_config.user %} +{%             endif %} +{%             if trap_config.auth.plaintext_password is vyos_defined or trap_config.auth.encrypted_password is vyos_defined %} +{%                 set options = options ~ ' -a ' ~ trap_config.auth.type %} +{%                 if trap_config.auth.plaintext_password is vyos_defined %} +{%                     set options = options ~ ' -A ' ~ trap_config.auth.plaintext_password %} +{%                 elif trap_config.auth.encrypted_password is vyos_defined %} +{%                     set options = options ~ ' -3m ' ~ trap_config.auth.encrypted_password %} +{%                 endif %} +{%                 if trap_config.privacy.plaintext_password is vyos_defined or trap_config.privacy.encrypted_password is vyos_defined %} +{%                     set options = options ~ ' -x ' ~ trap_config.privacy.type %} +{%                     if trap_config.privacy.plaintext_password is vyos_defined %} +{%                         set options = options ~ ' -X ' ~ trap_config.privacy.plaintext_password %} +{%                     elif trap_config.privacy.encrypted_password is vyos_defined %} +{%                         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 +{%     if v3.user is vyos_defined %} +{%         for user, user_config in v3.user.items() %} +group {{ user_config.group }} usm {{ user }} +{%         endfor %} +{%     endif %} +{# SNMPv3 end #} +{% endif %} + +{% if script_extensions.extension_name is vyos_defined %} +# extension scripts +{%     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/etc.snmpd.conf.tmpl b/data/templates/snmp/etc.snmpd.conf.tmpl deleted file mode 100644 index 510b35097..000000000 --- a/data/templates/snmp/etc.snmpd.conf.tmpl +++ /dev/null @@ -1,182 +0,0 @@ -### 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 - -# Remove all old ifTable entries with the same ifName as newly appeared -# interface (with different ifIndex) - this is the case on e.g. ppp interfaces -interface_replace_old yes - -######################## -# configurable section # -######################## - -# Default system description is VyOS version -sysDescr VyOS {{ version }} - -{% if description is vyos_defined %} -# Description -SysDescr {{ description }} -{% endif %} - -# Listen -{% set options = [] %} -{% if listen_address is vyos_defined %} -{%   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') %} -{%   set _ = options.append(protocol ~ '6:161') %} -{% endif %} -agentaddress unix:/run/snmpd.socket{{ ',' ~ options | join(',') if options is vyos_defined }} - -# SNMP communities -{% if community is vyos_defined %} -{%   for comm, comm_config in community.items() %} -{%     if comm_config.client is vyos_defined %} -{%       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 vyos_defined %} -{%       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 vyos_defined and comm_config.network is not vyos_defined %} -{{ comm_config.authorization }}community {{ comm }} -{%     endif %} -{%   endfor %} -{% endif %} - -{% if contact is vyos_defined %} -# system contact information -SysContact {{ contact }} -{% endif %} - -{% if location is vyos_defined %} -# system location information -SysLocation {{ location }} -{% endif %} - -{% if smux_peer is vyos_defined %} -# additional smux peers -{%   for peer in smux_peer %} -smuxpeer {{ peer }} -{%   endfor %} -{% endif %} - -{% if trap_target is vyos_defined %} -# if there is a problem - tell someone! -{%   for trap, trap_config in trap_target.items() %} -trap2sink {{ trap }}:{{ trap_config.port }} {{ trap_config.community }} -{%   endfor %} -{% endif %} - -{% if v3 is vyos_defined %} -# -# SNMPv3 stuff goes here -# -{%   if v3.view is vyos_defined %} -# views -{%     for view, view_config in v3.view.items() %} -{%       if view_config.oid is vyos_defined %} -{%         for oid in view_config.oid %} -view {{ view }} included .{{ oid }} -{%         endfor %} -{%       endif %} -{%     endfor %} -{%   endif %} - -# access -{%   if v3.group is vyos_defined %} -#             context sec.model sec.level match  read    write  notif -{%     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 -{%   if v3.trap_target is vyos_defined %} -{%     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 vyos_defined %} -{%         set options = options ~ ' -e "' ~ v3.engineid ~ '"' %} -{%       endif %} -{%       if trap_config.user is vyos_defined %} -{%         set options = options ~ ' -u ' ~ trap_config.user %} -{%       endif %} -{%       if trap_config.auth.plaintext_password is vyos_defined or trap_config.auth.encrypted_password is vyos_defined %} -{%         set options = options ~ ' -a ' ~ trap_config.auth.type %} -{%         if trap_config.auth.plaintext_password is vyos_defined %} -{%           set options = options ~ ' -A ' ~ trap_config.auth.plaintext_password %} -{%         elif trap_config.auth.encrypted_password is vyos_defined %} -{%           set options = options ~ ' -3m ' ~ trap_config.auth.encrypted_password %} -{%         endif %} -{%         if trap_config.privacy.plaintext_password is vyos_defined or trap_config.privacy.encrypted_password is vyos_defined %} -{%           set options = options ~ ' -x ' ~ trap_config.privacy.type %} -{%           if trap_config.privacy.plaintext_password is vyos_defined %} -{%             set options = options ~ ' -X ' ~ trap_config.privacy.plaintext_password %} -{%           elif trap_config.privacy.encrypted_password is vyos_defined %} -{%             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 -{%   if v3.user is vyos_defined %} -{%     for user, user_config in v3.user.items() %} -group {{ user_config.group }} usm {{ user }} -{%     endfor %} -{%   endif %} -{# SNMPv3 end #} -{% endif %} - -{% if script_extensions.extension_name is vyos_defined %} -# extension scripts -{%   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.j2 index 5d787de86..5d787de86 100644 --- a/data/templates/snmp/override.conf.tmpl +++ b/data/templates/snmp/override.conf.j2 diff --git a/data/templates/snmp/usr.snmpd.conf.tmpl b/data/templates/snmp/usr.snmpd.conf.j2 index a46b3997f..a713c1cec 100644 --- a/data/templates/snmp/usr.snmpd.conf.tmpl +++ b/data/templates/snmp/usr.snmpd.conf.j2 @@ -1,8 +1,8 @@  ### Autogenerated by snmp.py ### -{%   if v3.user is vyos_defined %} +{% if v3.user is vyos_defined %}  {%     for user, user_config in v3.user.items() %}  {{ user_config.mode }}user {{ user }}  {%     endfor %} -{%   endif %} +{% endif %}  rwuser {{ vyos_user }} diff --git a/data/templates/snmp/var.snmpd.conf.tmpl b/data/templates/snmp/var.snmpd.conf.j2 index 16d39db89..012f33aeb 100644 --- a/data/templates/snmp/var.snmpd.conf.tmpl +++ b/data/templates/snmp/var.snmpd.conf.j2 @@ -1,16 +1,16 @@  ### Autogenerated by snmp.py ###  # user  {% if v3 is vyos_defined %} -{%   if v3.user is vyos_defined %} -{%     for user, user_config in v3.user.items() %} +{%     if v3.user is vyos_defined %} +{%         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 %} +{%         endfor %} +{%     endif %}  # VyOS default user  createUser {{ vyos_user }} MD5 "{{ vyos_user_pass }}" DES -{%   if v3.engineid is vyos_defined %} +{%     if v3.engineid is vyos_defined %}  oldEngineID 0x{{ v3.engineid }} -{%   endif %} +{%     endif %}  {% endif %} diff --git a/data/templates/squid/sg_acl.conf.tmpl b/data/templates/squid/sg_acl.conf.j2 index ce72b173a..ce72b173a 100644 --- a/data/templates/squid/sg_acl.conf.tmpl +++ b/data/templates/squid/sg_acl.conf.j2 diff --git a/data/templates/squid/squid.conf.tmpl b/data/templates/squid/squid.conf.j2 index e8627b022..a0fdeb20e 100644 --- a/data/templates/squid/squid.conf.tmpl +++ b/data/templates/squid/squid.conf.j2 @@ -16,23 +16,23 @@ acl Safe_ports port 777         # multiling http  acl CONNECT method CONNECT  {% if authentication is vyos_defined %} -{%   if authentication.children is vyos_defined %} +{%     if authentication.children is vyos_defined %}  auth_param basic children {{ authentication.children }} -{%   endif %} -{%   if authentication.credentials_ttl is vyos_defined %} +{%     endif %} +{%     if authentication.credentials_ttl is vyos_defined %}  auth_param basic credentialsttl {{ authentication.credentials_ttl }} minute -{%   endif %} -{%   if authentication.realm is vyos_defined %} +{%     endif %} +{%     if authentication.realm is vyos_defined %}  auth_param basic realm "{{ authentication.realm }}" -{%   endif %} +{%     endif %}  {# LDAP based Authentication #} -{%   if authentication.method is vyos_defined %} -{%     if authentication.ldap is vyos_defined and authentication.method is vyos_defined('ldap') %} +{%     if authentication.method is vyos_defined %} +{%         if authentication.ldap is vyos_defined and authentication.method is vyos_defined('ldap') %}  auth_param basic program /usr/lib/squid/basic_ldap_auth -v {{ authentication.ldap.version }} -b "{{ authentication.ldap.base_dn }}" {{ '-D "' ~ authentication.ldap.bind_dn ~ '"' if authentication.ldap.bind_dn is vyos_defined }} {{ '-w "' ~ authentication.ldap.password ~ '"' if authentication.ldap.password is vyos_defined }} {{ '-f "' ~ authentication.ldap.filter_expression ~ '"' if authentication.ldap.filter_expression is vyos_defined }} {{ '-u "' ~ authentication.ldap.username_attribute ~ '"' if authentication.ldap.username_attribute is vyos_defined }} -p {{ authentication.ldap.port }} {{ '-ZZ' if authentication.ldap.use_ssl is vyos_defined }} -R -h "{{ authentication.ldap.server }}" -{%     endif %} +{%         endif %}  acl auth proxy_auth REQUIRED  http_access allow auth -{%   endif %} +{%     endif %}  {% endif %}  http_access allow manager localhost @@ -44,18 +44,18 @@ http_access allow net  http_access deny all  {% if reply_block_mime is vyos_defined %} -{%   for mime_type in reply_block_mime %} +{%     for mime_type in reply_block_mime %}  acl BLOCK_MIME rep_mime_type {{ mime_type }} -{%   endfor %} +{%     endfor %}  http_reply_access deny BLOCK_MIME  {% endif %}  {% if cache_size is vyos_defined %} -{%   if cache_size | int > 0 %} +{%     if cache_size | int > 0 %}  cache_dir ufs /var/spool/squid {{ cache_size }} 16 256 -{%   else %} +{%     else %}  # disabling disk cache -{%   endif %} +{%     endif %}  {% endif %}  {% if mem_cache_size is vyos_defined %}  cache_mem {{ mem_cache_size }} MB @@ -87,9 +87,9 @@ tcp_outgoing_address {{ outgoing_address }}  {% if listen_address is vyos_defined %} -{%   for address, config in listen_address.items() %} +{%     for address, config in listen_address.items() %}  http_port {{ address | bracketize_ipv6 }}:{{ config.port if config.port is vyos_defined else default_port }} {{ 'intercept' if config.disable_transparent is not vyos_defined }} -{%   endfor %} +{%     endfor %}  {% endif %}  http_port 127.0.0.1:{{ default_port }} @@ -104,8 +104,8 @@ url_rewrite_bypass on  {% endif %}  {% if cache_peer is vyos_defined %} -{%   for peer, config in cache_peer.items() %} +{%     for peer, config in cache_peer.items() %}  cache_peer {{ config.address }} {{ config.type }} {{ config.http_port }} {{ config.icp_port }} {{ config.options }} -{%   endfor %} +{%     endfor %}  never_direct allow all  {% endif %} diff --git a/data/templates/squid/squidGuard.conf.j2 b/data/templates/squid/squidGuard.conf.j2 new file mode 100644 index 000000000..1bc4c984f --- /dev/null +++ b/data/templates/squid/squidGuard.conf.j2 @@ -0,0 +1,124 @@ +### generated by service_webproxy.py ### + +{% macro sg_rule(category, log, db_dir) %} +{% set expressions = db_dir + '/' + category + '/expressions' %} +dest {{ category }}-default { +        domainlist     {{ category }}/domains +        urllist        {{ category }}/urls +{% if expressions | is_file %} +        expressionlist {{ category }}/expressions +{% endif %} +{% if log is vyos_defined %} +        log            blacklist.log +{% endif %} +} +{% endmacro %} + +{% if url_filtering is vyos_defined and url_filtering.disable is not vyos_defined %} +{%     if url_filtering.squidguard is vyos_defined %} +{%         set sg_config = url_filtering.squidguard %} +{%         set acl = namespace(value='local-ok-default') %} +{%         set acl.value = acl.value + ' !in-addr' if sg_config.allow_ipaddr_url is not defined else acl.value %} +dbhome {{ squidguard_db_dir }} +logdir /var/log/squid + +rewrite safesearch { +        s@(.*\.google\..*/(custom|search|images|groups|news)?.*q=.*)@\1\&safe=active@i +        s@(.*\..*/yandsearch?.*text=.*)@\1\&fyandex=1@i +        s@(.*\.yahoo\..*/search.*p=.*)@\1\&vm=r@i +        s@(.*\.live\..*/.*q=.*)@\1\&adlt=strict@i +        s@(.*\.msn\..*/.*q=.*)@\1\&adlt=strict@i +        s@(.*\.bing\..*/search.*q=.*)@\1\&adlt=strict@i +        log     rewrite.log +} + +{%         if sg_config.local_ok is vyos_defined %} +{%             set acl.value = acl.value + ' local-ok-default' %} +dest local-ok-default { +        domainlist     local-ok-default/domains +} +{%         endif %} +{%         if sg_config.local_ok_url is vyos_defined %} +{%             set acl.value = acl.value + ' local-ok-url-default' %} +dest local-ok-url-default { +        urllist        local-ok-url-default/urls +} +{%         endif %} +{%         if sg_config.local_block is vyos_defined %} +{%             set acl.value = acl.value + ' !local-block-default' %} +dest local-block-default { +        domainlist     local-block-default/domains +} +{%         endif %} +{%         if sg_config.local_block_url is vyos_defined %} +{%             set acl.value = acl.value + ' !local-block-url-default' %} +dest local-block-url-default { +        urllist        local-block-url-default/urls +} +{%         endif %} +{%         if sg_config.local_block_keyword is vyos_defined %} +{%             set acl.value = acl.value + ' !local-block-keyword-default' %} +dest local-block-keyword-default { +        expressionlist local-block-keyword-default/expressions +} +{%         endif %} + +{%         if sg_config.block_category is vyos_defined %} +{%             for category in sg_config.block_category %} +{{ sg_rule(category, sg_config.log, squidguard_db_dir) }} +{%                 set acl.value = acl.value + ' !' + category + '-default' %} +{%             endfor %} +{%         endif %} +{%         if sg_config.allow_category is vyos_defined %} +{%             for category in sg_config.allow_category %} +{{ sg_rule(category, False, squidguard_db_dir) }} +{%                 set acl.value = acl.value + ' ' + category + '-default' %} +{%             endfor %} +{%         endif %} +{%         if sg_config.source_group is vyos_defined %} +{%             for sgroup, sg_config in sg_config.source_group.items() %} +{%                 if sg_config.address is vyos_defined %} +src {{ sgroup }} { +{%                     for address in sg_config.address %} +        ip {{ address }} +{%                     endfor %} +} + +{%                 endif %} +{%             endfor %} +{%         endif %} +{%         if sg_config.rule is vyos_defined %} +{%             for rule, rule_config in sg_config.rule.items() %} +{%                 for b_category in rule_config.block_category %} +dest {{ b_category }} { +        domainlist    {{ b_category }}/domains +        urllist       {{ b_category }}/urls +} +{%                 endfor %} + +{%             endfor %} +{%         endif %} +acl { +{%         if sg_config.rule is vyos_defined %} +{%             for rule, rule_config in sg_config.rule.items() %} +        {{ rule_config.source_group }} { +{%                 for b_category in rule_config.block_category %} +            pass local-ok-1 !in-addr !{{ b_category }} all +{%                 endfor %} +        } +{%             endfor %} +{%         endif %} + +        default { +{%         if sg_config.enable_safe_search is vyos_defined %} +            rewrite safesearch +{%         endif %} +            pass {{ acl.value }} {{ 'none' if sg_config.default_action is vyos_defined('block') else 'allow' }} +            redirect 302:http://{{ sg_config.redirect_url }} +{%         if sg_config.log is vyos_defined %} +            log blacklist.log +{%         endif %} +        } +} +{%     endif %} +{% endif %} diff --git a/data/templates/squid/squidGuard.conf.tmpl b/data/templates/squid/squidGuard.conf.tmpl deleted file mode 100644 index 5e877f01f..000000000 --- a/data/templates/squid/squidGuard.conf.tmpl +++ /dev/null @@ -1,124 +0,0 @@ -### generated by service_webproxy.py ### - -{% macro sg_rule(category, log, db_dir) %} -{%   set expressions = db_dir + '/' + category + '/expressions' %} -dest {{ category }}-default { -        domainlist     {{ category }}/domains -        urllist        {{ category }}/urls -{%   if expressions | is_file %} -        expressionlist {{ category }}/expressions -{%   endif %} -{%   if log is vyos_defined %} -        log            blacklist.log -{%   endif %} -} -{% endmacro %} - -{% if url_filtering is vyos_defined and url_filtering.disable is not vyos_defined %} -{%   if url_filtering.squidguard is vyos_defined %} -{%     set sg_config = url_filtering.squidguard %} -{%     set acl = namespace(value='local-ok-default') %} -{%     set acl.value = acl.value + ' !in-addr' if sg_config.allow_ipaddr_url is not defined else acl.value %} -dbhome {{ squidguard_db_dir }} -logdir /var/log/squid - -rewrite safesearch { -        s@(.*\.google\..*/(custom|search|images|groups|news)?.*q=.*)@\1\&safe=active@i -        s@(.*\..*/yandsearch?.*text=.*)@\1\&fyandex=1@i -        s@(.*\.yahoo\..*/search.*p=.*)@\1\&vm=r@i -        s@(.*\.live\..*/.*q=.*)@\1\&adlt=strict@i -        s@(.*\.msn\..*/.*q=.*)@\1\&adlt=strict@i -        s@(.*\.bing\..*/search.*q=.*)@\1\&adlt=strict@i -        log     rewrite.log -} - -{%     if sg_config.local_ok is vyos_defined %} -{%       set acl.value = acl.value + ' local-ok-default' %} -dest local-ok-default { -        domainlist     local-ok-default/domains -} -{% endif %} -{%     if sg_config.local_ok_url is vyos_defined %} -{%       set acl.value = acl.value + ' local-ok-url-default' %} -dest local-ok-url-default { -        urllist        local-ok-url-default/urls -} -{% endif %} -{%     if sg_config.local_block is vyos_defined %} -{%       set acl.value = acl.value + ' !local-block-default' %} -dest local-block-default { -        domainlist     local-block-default/domains -} -{% endif %} -{%     if sg_config.local_block_url is vyos_defined %} -{%       set acl.value = acl.value + ' !local-block-url-default' %} -dest local-block-url-default { -        urllist        local-block-url-default/urls -} -{% endif %} -{%     if sg_config.local_block_keyword is vyos_defined %} -{%       set acl.value = acl.value + ' !local-block-keyword-default' %} -dest local-block-keyword-default { -        expressionlist local-block-keyword-default/expressions -} -{% endif %} - -{%     if sg_config.block_category is vyos_defined %} -{%       for category in sg_config.block_category %} -{{ sg_rule(category, sg_config.log, squidguard_db_dir) }} -{%         set acl.value = acl.value + ' !' + category + '-default' %} -{%       endfor %} -{%     endif %} -{%     if sg_config.allow_category is vyos_defined %} -{%       for category in sg_config.allow_category %} -{{ sg_rule(category, False, squidguard_db_dir) }} -{%         set acl.value = acl.value + ' ' + category + '-default' %} -{%       endfor %} -{%     endif %} -{%     if sg_config.source_group is vyos_defined %} -{%       for sgroup, sg_config in sg_config.source_group.items() %} -{%         if sg_config.address is vyos_defined %} -src {{ sgroup }} { -{%           for address in sg_config.address %} -        ip {{ address }} -{%           endfor %} -} - -{%         endif %} -{%       endfor %} -{%     endif %} -{%     if sg_config.rule is vyos_defined %} -{%       for rule, rule_config in sg_config.rule.items() %} -{%         for b_category in rule_config.block_category%} -dest {{ b_category }} { -        domainlist    {{ b_category }}/domains -        urllist       {{ b_category }}/urls -} -{%         endfor %} - -{%       endfor %} -{%     endif %} -acl { -{%     if sg_config.rule is vyos_defined %} -{%       for rule, rule_config in sg_config.rule.items() %} -        {{ rule_config.source_group }} { -{%         for b_category in rule_config.block_category%} -            pass local-ok-1 !in-addr !{{ b_category }} all -{%         endfor %} -        } -{%       endfor %} -{%     endif %} - -        default { -{%     if sg_config.enable_safe_search is vyos_defined %} -            rewrite safesearch -{%     endif %} -            pass {{ acl.value }} {{ 'none' if sg_config.default_action is vyos_defined('block') else 'allow' }} -            redirect 302:http://{{ sg_config.redirect_url }} -{%     if sg_config.log is vyos_defined %} -            log blacklist.log -{%     endif %} -        } -} -{%   endif %} -{% endif %} diff --git a/data/templates/ssh/sshguard_config.j2 b/data/templates/ssh/sshguard_config.j2 new file mode 100644 index 000000000..58c6ad48d --- /dev/null +++ b/data/templates/ssh/sshguard_config.j2 @@ -0,0 +1,27 @@ +### Autogenerated by ssh.py ### + +{% if dynamic_protection is vyos_defined %} +# Full path to backend executable (required, no default) +BACKEND="/usr/libexec/sshguard/sshg-fw-nft-sets" + +# Shell command that provides logs on standard output. (optional, no default) +# Example 1: ssh and sendmail from systemd journal: +LOGREADER="LANG=C journalctl -afb -p info -n1 -t sshd -o cat" + +#### OPTIONS #### +# Block attackers when their cumulative attack score exceeds THRESHOLD. +# Most attacks have a score of 10. (optional, default 30) +THRESHOLD={{ dynamic_protection.threshold }} + +# Block attackers for initially BLOCK_TIME seconds after exceeding THRESHOLD. +# Subsequent blocks increase by a factor of 1.5. (optional, default 120) +BLOCK_TIME={{ dynamic_protection.block_time }} + +# Remember potential attackers for up to DETECTION_TIME seconds before +# resetting their score. (optional, default 1800) +DETECTION_TIME={{ dynamic_protection.detect_time }} + +# IP addresses listed in the WHITELIST_FILE are considered to be +# friendlies and will never be blocked. +WHITELIST_FILE=/etc/sshguard/whitelist +{% endif %} diff --git a/data/templates/ssh/sshguard_whitelist.j2 b/data/templates/ssh/sshguard_whitelist.j2 new file mode 100644 index 000000000..47a950a2b --- /dev/null +++ b/data/templates/ssh/sshguard_whitelist.j2 @@ -0,0 +1,7 @@ +### Autogenerated by ssh.py ### + +{% if dynamic_protection.allow_from is vyos_defined %} +{%     for address in dynamic_protection.allow_from %} +{{ address }} +{%     endfor %} +{% endif %} diff --git a/data/templates/syslog/logrotate.tmpl b/data/templates/syslog/logrotate.j2 index c1b951e8b..c1b951e8b 100644 --- a/data/templates/syslog/logrotate.tmpl +++ b/data/templates/syslog/logrotate.j2 diff --git a/data/templates/syslog/rsyslog.conf.tmpl b/data/templates/syslog/rsyslog.conf.j2 index 2fb621760..4445d568b 100644 --- a/data/templates/syslog/rsyslog.conf.tmpl +++ b/data/templates/syslog/rsyslog.conf.j2 @@ -2,9 +2,9 @@  ## file based logging  {% if files['global']['marker'] %}  $ModLoad immark -{%   if files['global']['marker-interval'] %} -$MarkMessagePeriod  {{files['global']['marker-interval']}} -{%   endif %} +{%     if files['global']['marker-interval'] %} +$MarkMessagePeriod {{ files['global']['marker-interval'] }} +{%     endif %}  {% endif %}  {% if files['global']['preserver_fqdn'] %}  $PreserveFQDN on @@ -15,40 +15,40 @@ $outchannel {{ file }},{{ file_options['log-file'] }},{{ file_options['max-size'  {% endfor %}  {% if console is defined and console is not none %}  ## console logging -{%   for con, con_options in console.items() %} +{%     for con, con_options in console.items() %}  {{ con_options['selectors'] }} /dev/console -{%   endfor %} +{%     endfor %}  {% endif %}  {% if hosts is defined and hosts is not none %}  ## remote logging -{%   for host, host_options in hosts.items() %} -{%     if host_options.proto == 'tcp' %} -{%       if host_options.port is defined %} -{%         if host_options.oct_count is defined %} +{%     for host, host_options in hosts.items() %} +{%         if host_options.proto == 'tcp' %} +{%             if host_options.port is defined %} +{%                 if host_options.oct_count is defined %}  {{ host_options.selectors }} @@(o){{ host | bracketize_ipv6 }}:{{ host_options.port }};RSYSLOG_SyslogProtocol23Format -{%         else %} +{%                 else %}  {{ host_options.selectors }} @@{{ host | bracketize_ipv6 }}:{{ host_options.port }} -{%         endif %} -{%       else %} +{%                 endif %} +{%             else %}  {{ host_options.selectors }} @@{{ host | bracketize_ipv6 }} -{%       endif %} -{%     elif host_options.proto == 'udp' %} -{%       if host_options.port is defined %} +{%             endif %} +{%         elif host_options.proto == 'udp' %} +{%             if host_options.port is defined %}  {{ host_options.selectors }} @{{ host | bracketize_ipv6 }}:{{ host_options.port }}{{ ';RSYSLOG_SyslogProtocol23Format' if host_options.oct_count is sameas true }} -{%       else %} +{%             else %}  {{ host_options.selectors }} @{{ host | bracketize_ipv6 }} -{%       endif %} -{%     else %} -{%       if host_options['port'] %} +{%             endif %} +{%         else %} +{%             if host_options['port'] %}  {{ host_options.selectors }} @{{ host | bracketize_ipv6 }}:{{ host_options.port }} -{%       else %} +{%             else %}  {{ host_options.selectors }} @{{ host | bracketize_ipv6 }} -{%       endif %} -{%     endif %} -{%   endfor %} +{%             endif %} +{%         endif %} +{%     endfor %}  {% endif %}  {% if user is defined and user is not none %} -{%   for username, user_options in user.items() %} +{%     for username, user_options in user.items() %}  {{ user_options.selectors }} :omusrmsg:{{ username }} -{%   endfor %} +{%     endfor %}  {% endif %} diff --git a/data/templates/system/curlrc.tmpl b/data/templates/system/curlrc.j2 index be4efe8ba..be4efe8ba 100644 --- a/data/templates/system/curlrc.tmpl +++ b/data/templates/system/curlrc.j2 diff --git a/data/templates/system/ssh_config.tmpl b/data/templates/system/ssh_config.j2 index 1449f95b1..1449f95b1 100644 --- a/data/templates/system/ssh_config.tmpl +++ b/data/templates/system/ssh_config.j2 diff --git a/data/templates/system/sysctl.conf.tmpl b/data/templates/system/sysctl.conf.j2 index 3aa857647..59a19e157 100644 --- a/data/templates/system/sysctl.conf.tmpl +++ b/data/templates/system/sysctl.conf.j2 @@ -1,7 +1,7 @@  # autogenerated by system_sysctl.py
  {% if parameter is vyos_defined %}
 -{%   for k, v in parameter.items() %}
 +{%     for k, v in parameter.items() %}
  {{ k }} = {{ v.value }}
 -{%   endfor %}
 +{%     endfor %}
  {% endif %}
 diff --git a/data/templates/tftp-server/default.tmpl b/data/templates/tftp-server/default.j2 index 56784d467..b2676e0aa 100644 --- a/data/templates/tftp-server/default.tmpl +++ b/data/templates/tftp-server/default.j2 @@ -1,3 +1,4 @@ +{# j2lint: disable=jinja-variable-format #}  ### Autogenerated by tftp_server.py ###  DAEMON_ARGS="--listen --user tftp --address {{ listen_address }} {{ "--create --umask 000" if allow_upload is vyos_defined }} --secure {{ directory }}"  {% if vrf is vyos_defined %} diff --git a/data/templates/vrf/vrf.conf.tmpl b/data/templates/vrf/vrf.conf.j2 index a51e11ddf..d31d23574 100644 --- a/data/templates/vrf/vrf.conf.tmpl +++ b/data/templates/vrf/vrf.conf.j2 @@ -3,7 +3,7 @@  # Routing table ID to name mapping reference  # id       vrf name         comment  {% if name is vyos_defined %} -{%   for vrf, vrf_config in name.items() %} +{%     for vrf, vrf_config in name.items() %}  {{ "%-10s" | format(vrf_config.table) }} {{ "%-16s" | format(vrf) }} {{ '# ' ~ vrf_config.description if vrf_config.description is vyos_defined }} -{%   endfor %} +{%     endfor %}  {% endif %} diff --git a/data/templates/zone_policy/nftables.tmpl b/data/templates/zone_policy/nftables.j2 index 9e532b79e..e4c4dd7da 100644 --- a/data/templates/zone_policy/nftables.tmpl +++ b/data/templates/zone_policy/nftables.j2 @@ -1,113 +1,113 @@  #!/usr/sbin/nft -f  {% if cleanup_commands is vyos_defined %} -{%   for command in cleanup_commands %} +{%     for command in cleanup_commands %}  {{ command }} -{%   endfor %} +{%     endfor %}  {% endif %}  {% if zone is vyos_defined %}  table ip filter { -{%   for zone_name, zone_conf in zone.items() if zone_conf.ipv4 %} -{%     if zone_conf.local_zone is vyos_defined %} +{%     for zone_name, zone_conf in zone.items() if zone_conf.ipv4 %} +{%         if zone_conf.local_zone is vyos_defined %}      chain VZONE_{{ zone_name }}_IN {          iifname lo counter return -{%       for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall.name is vyos_defined %} +{%             for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall.name is vyos_defined %}          iifname { {{ zone[from_zone].interface | join(",") }} } counter jump NAME_{{ from_conf.firewall.name }}          iifname { {{ zone[from_zone].interface | join(",") }} } counter return -{%       endfor %} +{%             endfor %}          counter {{ zone_conf.default_action }}      }      chain VZONE_{{ zone_name }}_OUT {          oifname lo counter return -{%         for from_zone, from_conf in zone_conf.from_local.items() if from_conf.firewall.name is vyos_defined %} +{%             for from_zone, from_conf in zone_conf.from_local.items() if from_conf.firewall.name is vyos_defined %}          oifname { {{ zone[from_zone].interface | join(",") }} } counter jump NAME_{{ from_conf.firewall.name }}          oifname { {{ zone[from_zone].interface | join(",") }} } counter return -{%         endfor %} +{%             endfor %}          counter {{ zone_conf.default_action }}      } -{%     else %} +{%         else %}      chain VZONE_{{ zone_name }} {          iifname { {{ zone_conf.interface | join(",") }} } counter {{ zone_conf | nft_intra_zone_action(ipv6=False) }} -{%       if zone_conf.intra_zone_filtering is vyos_defined %} +{%             if zone_conf.intra_zone_filtering is vyos_defined %}          iifname { {{ zone_conf.interface | join(",") }} } counter return -{%       endif %} -{%       for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall.name is vyos_defined %} -{%         if zone[from_zone].local_zone is not defined %} +{%             endif %} +{%             for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall.name is vyos_defined %} +{%                 if zone[from_zone].local_zone is not defined %}          iifname { {{ zone[from_zone].interface | join(",") }} } counter jump NAME_{{ from_conf.firewall.name }}          iifname { {{ zone[from_zone].interface | join(",") }} } counter return -{%         endif %} -{%       endfor %} +{%                 endif %} +{%             endfor %}          counter {{ zone_conf.default_action }}      } -{%     endif %} -{%   endfor %} +{%         endif %} +{%     endfor %}  }  table ip6 filter { -{%   for zone_name, zone_conf in zone.items() if zone_conf.ipv6 %} -{%     if zone_conf.local_zone is vyos_defined %} +{%     for zone_name, zone_conf in zone.items() if zone_conf.ipv6 %} +{%         if zone_conf.local_zone is vyos_defined %}      chain VZONE6_{{ zone_name }}_IN {          iifname lo counter return -{%       for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall.ipv6_name is vyos_defined %} +{%             for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall.ipv6_name is vyos_defined %}          iifname { {{ zone[from_zone].interface | join(",") }} } counter jump NAME6_{{ from_conf.firewall.ipv6_name }}          iifname { {{ zone[from_zone].interface | join(",") }} } counter return -{%       endfor %} +{%             endfor %}          counter {{ zone_conf.default_action }}      }      chain VZONE6_{{ zone_name }}_OUT {          oifname lo counter return -{%         for from_zone, from_conf in zone_conf.from_local.items() if from_conf.firewall.ipv6_name is vyos_defined %} +{%             for from_zone, from_conf in zone_conf.from_local.items() if from_conf.firewall.ipv6_name is vyos_defined %}          oifname { {{ zone[from_zone].interface | join(",") }} } counter jump NAME6_{{ from_conf.firewall.ipv6_name }}          oifname { {{ zone[from_zone].interface | join(",") }} } counter return -{%         endfor %} +{%             endfor %}          counter {{ zone_conf.default_action }}      } -{%     else %} +{%         else %}      chain VZONE6_{{ zone_name }} {          iifname { {{ zone_conf.interface | join(",") }} } counter {{ zone_conf | nft_intra_zone_action(ipv6=True) }} -{%       if zone_conf.intra_zone_filtering is vyos_defined %} +{%             if zone_conf.intra_zone_filtering is vyos_defined %}          iifname { {{ zone_conf.interface | join(",") }} } counter return -{%       endif %} -{%       for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall.ipv6_name is vyos_defined %} -{%         if zone[from_zone].local_zone is not defined %} +{%             endif %} +{%             for from_zone, from_conf in zone_conf.from.items() if from_conf.firewall.ipv6_name is vyos_defined %} +{%                 if zone[from_zone].local_zone is not defined %}          iifname { {{ zone[from_zone].interface | join(",") }} } counter jump NAME6_{{ from_conf.firewall.ipv6_name }}          iifname { {{ zone[from_zone].interface | join(",") }} } counter return -{%         endif %} -{%       endfor %} +{%                 endif %} +{%             endfor %}          counter {{ zone_conf.default_action }}      } -{%     endif %} -{%   endfor %} +{%         endif %} +{%     endfor %}  } -{%   for zone_name, zone_conf in zone.items() %} -{%     if zone_conf.ipv4 %} -{%       if 'local_zone' in zone_conf %} +{%     for zone_name, zone_conf in zone.items() %} +{%         if zone_conf.ipv4 %} +{%             if 'local_zone' in zone_conf %}  insert rule ip filter VYOS_FW_LOCAL counter jump VZONE_{{ zone_name }}_IN  insert rule ip filter VYOS_FW_OUTPUT counter jump VZONE_{{ zone_name }}_OUT -{%       else %} +{%             else %}  insert rule ip filter VYOS_FW_FORWARD oifname { {{ zone_conf.interface | join(',') }} } counter jump VZONE_{{ zone_name }} -{%       endif %} -{%     endif %} -{%     if zone_conf.ipv6 %} -{%       if 'local_zone' in zone_conf %} +{%             endif %} +{%         endif %} +{%         if zone_conf.ipv6 %} +{%             if 'local_zone' in zone_conf %}  insert rule ip6 filter VYOS_FW6_LOCAL counter jump VZONE6_{{ zone_name }}_IN  insert rule ip6 filter VYOS_FW6_OUTPUT counter jump VZONE6_{{ zone_name }}_OUT -{%       else %} +{%             else %}  insert rule ip6 filter VYOS_FW6_FORWARD oifname { {{ zone_conf.interface | join(',') }} } counter jump VZONE6_{{ zone_name }} -{%       endif %} -{%     endif %} -{%   endfor %} +{%             endif %} +{%         endif %} +{%     endfor %}  {# Ensure that state-policy rule is first in the chain #} -{%   if firewall.state_policy is vyos_defined %} -{%     for chain in ['VYOS_FW_FORWARD', 'VYOS_FW_OUTPUT', 'VYOS_FW_LOCAL'] %} +{%     if firewall.state_policy is vyos_defined %} +{%         for chain in ['VYOS_FW_FORWARD', 'VYOS_FW_OUTPUT', 'VYOS_FW_LOCAL'] %}  insert rule ip filter {{ chain }} jump VYOS_STATE_POLICY -{%     endfor %} -{%     for chain in ['VYOS_FW6_FORWARD', 'VYOS_FW6_OUTPUT', 'VYOS_FW6_LOCAL'] %} +{%         endfor %} +{%         for chain in ['VYOS_FW6_FORWARD', 'VYOS_FW6_OUTPUT', 'VYOS_FW6_LOCAL'] %}  insert rule ip6 filter {{ chain }} jump VYOS_STATE_POLICY6 -{%     endfor %} -{%   endif %} +{%         endfor %} +{%     endif %}  {% endif %} diff --git a/debian/control b/debian/control index c53e4d3b8..bcd5acfdd 100644 --- a/debian/control +++ b/debian/control @@ -147,6 +147,7 @@ Depends:    squid,    squidclient,    squidguard, +  sshguard,    ssl-cert,    strongswan (>= 5.9),    strongswan-swanctl (>= 5.9), diff --git a/interface-definitions/containers.xml.in b/interface-definitions/container.xml.in index 85231b50c..51171d881 100644 --- a/interface-definitions/containers.xml.in +++ b/interface-definitions/container.xml.in @@ -1,6 +1,6 @@  <?xml version="1.0"?>  <interfaceDefinition> -  <node name="container" owner="${vyos_conf_scripts_dir}/containers.py"> +  <node name="container" owner="${vyos_conf_scripts_dir}/container.py">      <properties>        <help>Container applications</help>        <priority>1280</priority> diff --git a/interface-definitions/flow-accounting-conf.xml.in b/interface-definitions/flow-accounting-conf.xml.in index fc59f8ab3..878566b3f 100644 --- a/interface-definitions/flow-accounting-conf.xml.in +++ b/interface-definitions/flow-accounting-conf.xml.in @@ -220,7 +220,7 @@                </leafNode>                <tagNode name="server">                  <properties> -                  <help>Server to export NetFlow [REQUIRED]</help> +                  <help>NetFlow destination server</help>                    <valueHelp>                      <format>ipv4</format>                      <description>IPv4 server to export NetFlow</description> @@ -398,7 +398,7 @@                </leafNode>                <tagNode name="server">                  <properties> -                  <help>Server to export sFlow [REQUIRED]</help> +                  <help>sFlow destination server</help>                    <valueHelp>                      <format>ipv4</format>                      <description>IPv4 server to export sFlow</description> diff --git a/interface-definitions/include/accel-ppp/radius-additions-rate-limit.xml.i b/interface-definitions/include/accel-ppp/radius-additions-rate-limit.xml.i index be49fce5a..f44920c3f 100644 --- a/interface-definitions/include/accel-ppp/radius-additions-rate-limit.xml.i +++ b/interface-definitions/include/accel-ppp/radius-additions-rate-limit.xml.i @@ -21,6 +21,20 @@          <valueless />        </properties>      </leafNode> +    <leafNode name="multiplier"> +      <properties> +        <help>Shaper multiplier</help> +        <valueHelp> +          <format><0.001-1000></format> +          <description>Shaper multiplier</description> +        </valueHelp> +        <constraint> +          <validator name="numeric" argument="--range 0.001-1000 --float"/> +        </constraint> +        <constraintErrorMessage>Multiplier needs to be between 0.001 and 1000</constraintErrorMessage> +      </properties> +      <defaultValue>1</defaultValue> +    </leafNode>    </children>  </node>  <!-- include end --> diff --git a/interface-definitions/include/auth-local-users.xml.i b/interface-definitions/include/auth-local-users.xml.i index cb456eecf..9fb507474 100644 --- a/interface-definitions/include/auth-local-users.xml.i +++ b/interface-definitions/include/auth-local-users.xml.i @@ -19,74 +19,6 @@              <help>Password used for authentication</help>            </properties>          </leafNode> -        <node name="otp"> -          <properties> -            <help>2FA OTP authentication parameters</help> -          </properties> -          <children> -            <leafNode name="key"> -              <properties> -                <help>Token Key Secret key for the token algorithm (see RFC 4226)</help> -                <valueHelp> -                  <format>txt</format> -                  <description>OTP key in hex-encoded format</description> -                </valueHelp> -                <constraint> -                  <regex>[a-fA-F0-9]{20,10000}</regex> -                </constraint> -                <constraintErrorMessage>Key name must only include hex characters and be at least 20 characters long</constraintErrorMessage> -              </properties> -            </leafNode> -            <leafNode name="otp-length"> -              <properties> -                <help>Number of digits in OTP code</help> -                <valueHelp> -                  <format>u32:6-8</format> -                  <description>Number of digits in OTP code</description> -                </valueHelp> -                <constraint> -                  <validator name="numeric" argument="--range 6-8"/> -                </constraint> -                <constraintErrorMessage>Number of digits in OTP code must be between 6 and 8</constraintErrorMessage> -              </properties> -              <defaultValue>6</defaultValue> -            </leafNode> -            <leafNode name="interval"> -              <properties> -                <help>Time tokens interval in seconds</help> -                <valueHelp> -                  <format>u32:5-86400</format> -                  <description>Time tokens interval in seconds.</description> -                </valueHelp> -                <constraint> -                  <validator name="numeric" argument="--range 5-86400"/> -                </constraint> -                <constraintErrorMessage>Time token interval must be between 5 and 86400 seconds</constraintErrorMessage> -              </properties> -              <defaultValue>30</defaultValue> -            </leafNode> -            <leafNode name="token-type"> -              <properties> -                <help>Token type</help> -                <valueHelp> -                  <format>hotp-time</format> -                  <description>Time-based OTP algorithm</description> -                </valueHelp> -                <valueHelp> -                  <format>hotp-event</format> -                  <description>Event-based OTP algorithm</description> -                </valueHelp> -                <constraint> -                  <regex>(hotp-time|hotp-event)</regex> -                </constraint> -                <completionHelp> -                  <list>hotp-time hotp-event</list> -                </completionHelp> -              </properties> -              <defaultValue>hotp-time</defaultValue> -            </leafNode> -          </children> -        </node>        </children>      </tagNode>    </children> diff --git a/interface-definitions/include/firewall/common-rule.xml.i b/interface-definitions/include/firewall/common-rule.xml.i index cbdfa9dc2..2a5137dbf 100644 --- a/interface-definitions/include/firewall/common-rule.xml.i +++ b/interface-definitions/include/firewall/common-rule.xml.i @@ -95,6 +95,32 @@      </constraint>    </properties>  </leafNode> +<node name="connection-status"> +  <properties> +    <help>Connection status</help> +  </properties> +  <children> +    <leafNode name="nat"> +      <properties> +        <help>NAT connection status</help> +        <completionHelp> +          <list>destination source</list> +        </completionHelp> +        <valueHelp> +          <format>destination</format> +          <description>Match connections that are subject to destination NAT</description> +        </valueHelp> +        <valueHelp> +          <format>source</format> +          <description>Match connections that are subject to source NAT</description> +        </valueHelp> +        <constraint> +          <regex>^(destination|source)$</regex> +        </constraint> +      </properties> +    </leafNode> +  </children> +</node>  <leafNode name="protocol">    <properties>      <help>Protocol to match (protocol name, number, or "all")</help> diff --git a/interface-definitions/include/ipsec/local-address.xml.i b/interface-definitions/include/ipsec/local-address.xml.i index dc5653ce7..9d267f3f7 100644 --- a/interface-definitions/include/ipsec/local-address.xml.i +++ b/interface-definitions/include/ipsec/local-address.xml.i @@ -4,6 +4,7 @@      <help>IPv4 or IPv6 address of a local interface to use for VPN</help>      <completionHelp>        <list>any</list> +      <script>${vyos_completion_dir}/list_local_ips.sh --both</script>      </completionHelp>      <valueHelp>        <format>ipv4</format> diff --git a/interface-definitions/include/monitoring/url.xml.i b/interface-definitions/include/monitoring/url.xml.i new file mode 100644 index 000000000..32c81122d --- /dev/null +++ b/interface-definitions/include/monitoring/url.xml.i @@ -0,0 +1,15 @@ +<!-- include start from monitoring/url.xml.i --> +<leafNode name="url"> +  <properties> +    <help>Remote URL [REQUIRED]</help> +    <valueHelp> +      <format>url</format> +      <description>Remote URL</description> +    </valueHelp> +    <constraint> +      <regex>(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}?(\/.*)?</regex> +    </constraint> +    <constraintErrorMessage>Incorrect URL format</constraintErrorMessage> +  </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/version/quagga-version.xml.i b/interface-definitions/include/version/quagga-version.xml.i index bb8ad7f82..f9944acce 100644 --- a/interface-definitions/include/version/quagga-version.xml.i +++ b/interface-definitions/include/version/quagga-version.xml.i @@ -1,3 +1,3 @@  <!-- include start from include/version/quagga-version.xml.i --> -<syntaxVersion component='quagga' version='9'></syntaxVersion> +<syntaxVersion component='quagga' version='10'></syntaxVersion>  <!-- include end --> diff --git a/interface-definitions/policy-local-route.xml.in b/interface-definitions/policy-local-route.xml.in index 573a7963f..d969613b1 100644 --- a/interface-definitions/policy-local-route.xml.in +++ b/interface-definitions/policy-local-route.xml.in @@ -146,11 +146,11 @@                  <properties>                    <help>Source address or prefix</help>                    <valueHelp> -                    <format>ipv4</format> +                    <format>ipv6</format>                      <description>Address to match against</description>                    </valueHelp>                    <valueHelp> -                    <format>ipv4net</format> +                    <format>ipv6net</format>                      <description>Prefix to match against</description>                    </valueHelp>                    <constraint> diff --git a/interface-definitions/policy.xml.in b/interface-definitions/policy.xml.in index ddb0159d5..50b7cbc84 100644 --- a/interface-definitions/policy.xml.in +++ b/interface-definitions/policy.xml.in @@ -948,24 +948,49 @@                        </leafNode>                      </children>                    </node> -                  <leafNode name="as-path-exclude"> +                  <node name="as-path">                      <properties> -                      <help>Remove ASN(s) from a Border Gateway Protocol (BGP) AS-path attribute</help> -                      <valueHelp> -                        <format>txt</format> -                        <description>BGP AS path exclude string (ex: "456 64500 45001")</description> -                      </valueHelp> -                    </properties> -                  </leafNode> -                  <leafNode name="as-path-prepend"> -                    <properties> -                      <help>Prepend string for a Border Gateway Protocol (BGP) AS-path attribute</help> -                      <valueHelp> -                        <format>txt</format> -                        <description>BGP AS path prepend string (ex: "64501 64501")</description> -                      </valueHelp> +                      <help>Transform BGP AS_PATH attribute</help>                      </properties> -                  </leafNode> +                    <children> +                      <leafNode name="exclude"> +                        <properties> +                          <help>Remove/exclude from the as-path attribute</help> +                          <valueHelp> +                            <format>u32</format> +                            <description>AS number</description> +                          </valueHelp> +                          <constraint> +                            <validator name="as-number-list"/> +                          </constraint> +                        </properties> +                      </leafNode> +                      <leafNode name="prepend"> +                        <properties> +                          <help>Prepend to the as-path</help> +                          <valueHelp> +                            <format>u32</format> +                            <description>AS number</description> +                          </valueHelp> +                          <constraint> +                            <validator name="as-number-list"/> +                          </constraint> +                        </properties> +                      </leafNode> +                      <leafNode name="prepend-last-as"> +                        <properties> +                          <help>Use the last AS-number in the as-path</help> +                          <valueHelp> +                            <format>u32:1-10</format> +                            <description>Number of times to insert</description> +                          </valueHelp> +                          <constraint> +                            <validator name="numeric" argument="--range 1-10"/> +                          </constraint> +                        </properties> +                      </leafNode> +                    </children> +                  </node>                    <leafNode name="atomic-aggregate">                      <properties>                        <help>BGP atomic aggregate attribute</help> @@ -1045,6 +1070,44 @@                        </constraint>                      </properties>                    </leafNode> +                  <node name="evpn"> +                    <properties> +                      <help>Ethernet Virtual Private Network</help> +                    </properties> +                    <children> +                      <node name="gateway"> +                        <properties> +                          <help>Set gateway IP for prefix advertisement route</help> +                        </properties> +                        <children> +                          <leafNode name="ipv4"> +                            <properties> +                              <help>Set gateway IPv4 address</help> +                              <valueHelp> +                                <format>ipv4</format> +                                <description>Gateway IPv4 address</description> +                              </valueHelp> +                              <constraint> +                                <validator name="ipv4-address"/> +                              </constraint> +                            </properties> +                          </leafNode> +                          <leafNode name="ipv6"> +                            <properties> +                              <help>Set gateway IPv6 address</help> +                              <valueHelp> +                                <format>ipv6</format> +                                <description>Gateway IPv6 address</description> +                              </valueHelp> +                              <constraint> +                                <validator name="ipv6-address"/> +                              </constraint> +                            </properties> +                          </leafNode> +                        </children> +                      </node> +                    </children> +                  </node>                    <node name="extcommunity">                      <properties>                        <help>BGP extended community attribute</help> diff --git a/interface-definitions/protocols-nhrp.xml.in b/interface-definitions/protocols-nhrp.xml.in index 7de3704ce..1e08c6873 100644 --- a/interface-definitions/protocols-nhrp.xml.in +++ b/interface-definitions/protocols-nhrp.xml.in @@ -4,7 +4,7 @@      <children>        <node name="nhrp" owner="${vyos_conf_scripts_dir}/protocols_nhrp.py">          <properties> -          <help>NHRP parameters</help> +          <help>Next Hop Resolution Protocol (NHRP) parameters</help>            <priority>680</priority>          </properties>          <children> diff --git a/interface-definitions/service_conntrack-sync.xml.in b/interface-definitions/service_conntrack-sync.xml.in index 32efa7323..6fa6fc5f9 100644 --- a/interface-definitions/service_conntrack-sync.xml.in +++ b/interface-definitions/service_conntrack-sync.xml.in @@ -5,7 +5,8 @@        <node name="conntrack-sync" owner="${vyos_conf_scripts_dir}/conntrack_sync.py">          <properties>            <help>Connection tracking synchronization</help> -          <priority>995</priority> +          <!-- before VRRP / HA --> +          <priority>799</priority>          </properties>          <children>            <leafNode name="accept-protocol"> diff --git a/interface-definitions/service_monitoring_telegraf.xml.in b/interface-definitions/service_monitoring_telegraf.xml.in index 01bc16fc9..bd528ea33 100644 --- a/interface-definitions/service_monitoring_telegraf.xml.in +++ b/interface-definitions/service_monitoring_telegraf.xml.in @@ -42,6 +42,94 @@                    </leafNode>                  </children>                </node> +              <node name="azure-data-explorer"> +                <properties> +                  <help>Output plugin Azure Data Explorer</help> +                </properties> +                <children> +                  <node name="authentication"> +                    <properties> +                      <help>Authentication parameters</help> +                    </properties> +                    <children> +                      <leafNode name="client-id"> +                        <properties> +                          <help>Application client id</help> +                          <constraint> +                            <regex>[-_a-zA-Z0-9]+</regex> +                          </constraint> +                          <constraintErrorMessage>Client-id is limited to alphanumerical characters and can contain hyphen and underscores</constraintErrorMessage> +                        </properties> +                      </leafNode> +                      <leafNode name="client-secret"> +                        <properties> +                          <help>Application client secret</help> +                          <constraint> +                            <regex>[-_a-zA-Z0-9]+</regex> +                          </constraint> +                          <constraintErrorMessage>Client-secret is limited to alphanumerical characters and can contain hyphen and underscores</constraintErrorMessage> +                        </properties> +                      </leafNode> +                      <leafNode name="tenant-id"> +                        <properties> +                          <help>Set tenant id</help> +                          <constraint> +                            <regex>[-_a-zA-Z0-9]+</regex> +                          </constraint> +                          <constraintErrorMessage>Tenant-id is limited to alphanumerical characters and can contain hyphen and underscores</constraintErrorMessage> +                        </properties> +                      </leafNode> +                    </children> +                  </node> +                  <leafNode name="database"> +                    <properties> +                      <help>Remote database name [REQUIRED]</help> +                      <valueHelp> +                        <format>txt</format> +                        <description>Remote database name</description> +                      </valueHelp> +                      <constraint> +                        <regex>[-_a-zA-Z0-9]+</regex> +                      </constraint> +                      <constraintErrorMessage>Database is limited to alphanumerical characters and can contain hyphen and underscores</constraintErrorMessage> +                    </properties> +                  </leafNode> +                  <leafNode name="group-metrics"> +                    <properties> +                      <help>Type of metrics grouping when push to Azure Data Explorer</help> +                      <completionHelp> +                        <list>single-table table-per-metric</list> +                      </completionHelp> +                      <valueHelp> +                        <format>single-table</format> +                        <description>Metrics stores in one table</description> +                      </valueHelp> +                      <valueHelp> +                        <format>table-per-metric</format> +                        <description>One table per gorups of metric by the metric name</description> +                      </valueHelp> +                      <constraint> +                        <regex>(single-table|table-per-metric)</regex> +                      </constraint> +                    </properties> +                    <defaultValue>table-per-metric</defaultValue> +                  </leafNode> +                  <leafNode name="table"> +                    <properties> +                      <help>Name of the single table [Only if set group-metrics single-table]</help> +                      <valueHelp> +                        <format>txt</format> +                        <description>Table name</description> +                      </valueHelp> +                      <constraint> +                        <regex>[-_a-zA-Z0-9]+</regex> +                      </constraint> +                      <constraintErrorMessage>Table is limited to alphanumerical characters and can contain hyphen and underscores</constraintErrorMessage> +                    </properties> +                  </leafNode> +                  #include <include/monitoring/url.xml.i> +                </children> +              </node>                <leafNode name="bucket">                  <properties>                    <help>Remote bucket</help> @@ -85,19 +173,128 @@                  </properties>                  <defaultValue>all</defaultValue>                </leafNode> -              <leafNode name="url"> +              <node name="prometheus-client">                  <properties> -                  <help>Remote URL [REQUIRED]</help> -                  <valueHelp> -                    <format>url</format> -                    <description>Remote URL to InfluxDB v2</description> -                  </valueHelp> -                  <constraint> -                    <regex>(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}?(\/.*)?</regex> -                  </constraint> -                  <constraintErrorMessage>Incorrect URL format.</constraintErrorMessage> +                  <help>Output plugin Prometheus client</help>                  </properties> -              </leafNode> +                <children> +                  <node name="authentication"> +                    <properties> +                      <help>HTTP basic authentication parameters</help> +                    </properties> +                    <children> +                      <leafNode name="username"> +                        <properties> +                          <help>Authentication username</help> +                        </properties> +                      </leafNode> +                      <leafNode name="password"> +                        <properties> +                          <help>Authentication password</help> +                          <valueHelp> +                            <format>txt</format> +                            <description>Authentication password</description> +                          </valueHelp> +                        </properties> +                      </leafNode> +                    </children> +                  </node> +                  <leafNode name="allow-from"> +                    <properties> +                      <help>Networks allowed to query this server</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> +                      <multi/> +                      <constraint> +                        <validator name="ip-prefix"/> +                      </constraint> +                    </properties> +                  </leafNode> +                  <leafNode name="listen-address"> +                    <properties> +                      <help>Local IP addresses to listen on</help> +                      <completionHelp> +                        <script>${vyos_completion_dir}/list_local_ips.sh --both</script> +                      </completionHelp> +                      <valueHelp> +                        <format>ipv4</format> +                        <description>IPv4 address to listen for incoming connections</description> +                      </valueHelp> +                      <valueHelp> +                        <format>ipv6</format> +                        <description>IPv6 address to listen for incoming connections</description> +                      </valueHelp> +                      <constraint> +                        <validator name="ipv4-address"/> +                        <validator name="ipv6-address"/> +                        <validator name="ipv6-link-local"/> +                      </constraint> +                    </properties> +                  </leafNode> +                  <leafNode name="metric-version"> +                    <properties> +                      <help>Metric version control mapping from Telegraf to Prometheus format</help> +                      <valueHelp> +                        <format>u32:1-2</format> +                        <description>Metric version (default: 2)</description> +                      </valueHelp> +                      <constraint> +                        <validator name="numeric" argument="--range 1-2"/> +                      </constraint> +                    </properties> +                    <defaultValue>2</defaultValue> +                  </leafNode> +                  #include <include/port-number.xml.i> +                  <leafNode name="port"> +                    <defaultValue>9273</defaultValue> +                  </leafNode> +                </children> +              </node> +              <node name="splunk"> +                <properties> +                  <help>Output plugin Splunk</help> +                </properties> +                <children> +                  <node name="authentication"> +                    <properties> +                      <help>HTTP basic authentication parameters</help> +                    </properties> +                    <children> +                      <leafNode name="token"> +                        <properties> +                          <help>Authorization token</help> +                        </properties> +                      </leafNode> +                      <leafNode name="insecure"> +                        <properties> +                          <help>Use TLS but skip host validation</help> +                          <valueless/> +                        </properties> +                      </leafNode> +                    </children> +                  </node> +                  <leafNode name="url"> +                    <properties> +                      <help>Remote URL [REQUIRED]</help> +                      <valueHelp> +                        <format>url</format> +                        <description>Remote URL to Splunk collector</description> +                      </valueHelp> +                      <constraint> +                        <regex>^(http(s?):\/\/.*):(\d*)\/?(.*)</regex> +                      </constraint> +                      <constraintErrorMessage>Incorrect URL format</constraintErrorMessage> +                    </properties> +                  </leafNode> +                </children> +              </node> +              #include <include/monitoring/url.xml.i>                #include <include/port-number.xml.i>                <leafNode name="port">                  <defaultValue>8086</defaultValue> diff --git a/interface-definitions/ssh.xml.in b/interface-definitions/ssh.xml.in index 8edbad110..126183162 100644 --- a/interface-definitions/ssh.xml.in +++ b/interface-definitions/ssh.xml.in @@ -61,6 +61,78 @@                <valueless/>              </properties>            </leafNode> +          <node name="dynamic-protection"> +            <properties> +              <help>Allow dynamic protection</help> +            </properties> +            <children> +              <leafNode name="block-time"> +                <properties> +                  <help>Block source IP in seconds. Subsequent blocks increase by a factor of 1.5</help> +                  <valueHelp> +                    <format>u32:1-65535</format> +                    <description>Time interval in seconds for blocking</description> +                  </valueHelp> +                  <constraint> +                    <validator name="numeric" argument="--range 1-65535"/> +                  </constraint> +                </properties> +                <defaultValue>120</defaultValue> +              </leafNode> +              <leafNode name="detect-time"> +                <properties> +                  <help>Remember source IP in seconds before reset their score</help> +                  <valueHelp> +                    <format>u32:1-65535</format> +                    <description>Time interval in seconds</description> +                  </valueHelp> +                  <constraint> +                    <validator name="numeric" argument="--range 1-65535"/> +                  </constraint> +                </properties> +                <defaultValue>1800</defaultValue> +              </leafNode> +              <leafNode name="threshold"> +                <properties> +                  <help>Block source IP when their cumulative attack score exceeds threshold</help> +                  <valueHelp> +                    <format>u32:1-65535</format> +                    <description>Threshold score</description> +                  </valueHelp> +                  <constraint> +                    <validator name="numeric" argument="--range 1-65535"/> +                  </constraint> +                </properties> +                <defaultValue>30</defaultValue> +              </leafNode> +              <leafNode name="allow-from"> +                <properties> +                  <help>Always allow inbound connections from these systems</help> +                  <valueHelp> +                    <format>ipv4</format> +                    <description>Address to match against</description> +                  </valueHelp> +                  <valueHelp> +                    <format>ipv4net</format> +                    <description>IPv4 address and prefix length</description> +                  </valueHelp> +                  <valueHelp> +                    <format>ipv6</format> +                    <description>IPv6 address to match against</description> +                  </valueHelp> +                  <valueHelp> +                    <format>ipv6net</format> +                    <description>IPv6 address and prefix length</description> +                  </valueHelp> +                  <constraint> +                    <validator name="ip-address"/> +                    <validator name="ip-prefix"/> +                  </constraint> +                  <multi/> +                </properties> +              </leafNode> +            </children> +          </node>            <leafNode name="key-exchange">              <properties>                <help>Allowed key exchange (KEX) algorithms</help> diff --git a/interface-definitions/system-frr.xml.in b/interface-definitions/system-frr.xml.in new file mode 100644 index 000000000..9fe23ed75 --- /dev/null +++ b/interface-definitions/system-frr.xml.in @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interfaceDefinition> +  <node name="system"> +    <children> +      <node name="frr" owner="${vyos_conf_scripts_dir}/system_frr.py"> +        <properties> +          <help>Configure FRR parameters</help> +          <!-- Before components that use FRR --> +          <priority>150</priority> +        </properties> +        <children> +          <leafNode name="bmp"> +            <properties> +              <help>Enable BGP Monitoring Protocol support</help> +              <valueless/> +            </properties> +          </leafNode> +          <leafNode name="irdp"> +            <properties> +              <help>Enable ICMP Router Discovery Protocol support</help> +              <valueless/> +            </properties> +          </leafNode> +          <node name="snmp"> +            <properties> +              <help>Enable SNMP integration for next daemons</help> +            </properties> +            <children> +              <leafNode name="bgpd"> +                <properties> +                  <help>BGP</help> +                  <valueless/> +                </properties> +              </leafNode> +              <leafNode name="isisd"> +                <properties> +                  <help>IS-IS</help> +                  <valueless/> +                </properties> +              </leafNode> +              <leafNode name="ldpd"> +                <properties> +                  <help>LDP</help> +                  <valueless/> +                </properties> +              </leafNode> +              <leafNode name="ospf6d"> +                <properties> +                  <help>OSPFv3</help> +                  <valueless/> +                </properties> +              </leafNode> +              <leafNode name="ospfd"> +                <properties> +                  <help>OSPFv2</help> +                  <valueless/> +                </properties> +              </leafNode> +              <leafNode name="ripd"> +                <properties> +                  <help>RIP</help> +                  <valueless/> +                </properties> +              </leafNode> +              <leafNode name="zebra"> +                <properties> +                  <help>Zebra (IP routing manager)</help> +                  <valueless/> +                </properties> +              </leafNode> +            </children> +          </node> +        </children> +      </node> +    </children> +  </node> +</interfaceDefinition> diff --git a/interface-definitions/vpn_openconnect.xml.in b/interface-definitions/vpn_openconnect.xml.in index 7981c3fa2..21b47125d 100644 --- a/interface-definitions/vpn_openconnect.xml.in +++ b/interface-definitions/vpn_openconnect.xml.in @@ -51,6 +51,82 @@                  </children>                </node>                #include <include/auth-local-users.xml.i> +              <node name="local-users"> +                <children> +                  <tagNode name="username"> +                    <children> +                        <node name="otp"> +                          <properties> +                            <help>2FA OTP authentication parameters</help> +                          </properties> +                          <children> +                            <leafNode name="key"> +                              <properties> +                                <help>Token Key Secret key for the token algorithm (see RFC 4226)</help> +                                <valueHelp> +                                  <format>txt</format> +                                  <description>OTP key in hex-encoded format</description> +                                </valueHelp> +                                <constraint> +                                  <regex>[a-fA-F0-9]{20,10000}</regex> +                                </constraint> +                                <constraintErrorMessage>Key name must only include hex characters and be at least 20 characters long</constraintErrorMessage> +                              </properties> +                            </leafNode> +                            <leafNode name="otp-length"> +                              <properties> +                                <help>Number of digits in OTP code</help> +                                <valueHelp> +                                  <format>u32:6-8</format> +                                  <description>Number of digits in OTP code</description> +                                </valueHelp> +                                <constraint> +                                  <validator name="numeric" argument="--range 6-8"/> +                                </constraint> +                                <constraintErrorMessage>Number of digits in OTP code must be between 6 and 8</constraintErrorMessage> +                              </properties> +                              <defaultValue>6</defaultValue> +                            </leafNode> +                            <leafNode name="interval"> +                              <properties> +                                <help>Time tokens interval in seconds</help> +                                <valueHelp> +                                  <format>u32:5-86400</format> +                                  <description>Time tokens interval in seconds.</description> +                                </valueHelp> +                                <constraint> +                                  <validator name="numeric" argument="--range 5-86400"/> +                                </constraint> +                                <constraintErrorMessage>Time token interval must be between 5 and 86400 seconds</constraintErrorMessage> +                              </properties> +                              <defaultValue>30</defaultValue> +                            </leafNode> +                            <leafNode name="token-type"> +                              <properties> +                                <help>Token type</help> +                                <valueHelp> +                                  <format>hotp-time</format> +                                  <description>Time-based OTP algorithm</description> +                                </valueHelp> +                                <valueHelp> +                                  <format>hotp-event</format> +                                  <description>Event-based OTP algorithm</description> +                                </valueHelp> +                                <constraint> +                                  <regex>(hotp-time|hotp-event)</regex> +                                </constraint> +                                <completionHelp> +                                  <list>hotp-time hotp-event</list> +                                </completionHelp> +                              </properties> +                              <defaultValue>hotp-time</defaultValue> +                            </leafNode> +                          </children> +                        </node> +                    </children> +                  </tagNode> +                </children> +              </node>                #include <include/radius-server-ipv4.xml.i>                <node name="radius">                  <children> diff --git a/interface-definitions/vrf.xml.in b/interface-definitions/vrf.xml.in index 14c31fa8a..25a573887 100644 --- a/interface-definitions/vrf.xml.in +++ b/interface-definitions/vrf.xml.in @@ -28,6 +28,22 @@          <children>            #include <include/interface/description.xml.i>            #include <include/interface/disable.xml.i> +          <node name="ip"> +            <properties> +              <help>IPv4 routing parameters</help> +            </properties> +            <children> +              #include <include/interface/disable-forwarding.xml.i> +            </children> +          </node> +          <node name="ipv6"> +            <properties> +              <help>IPv6 routing parameters</help> +            </properties> +            <children> +              #include <include/interface/disable-forwarding.xml.i> +            </children> +          </node>            <node name="protocols">              <properties>                <help>Routing protocol parameters</help> diff --git a/op-mode-definitions/containers.xml.in b/op-mode-definitions/container.xml.in index 48501bd84..fa66402dc 100644 --- a/op-mode-definitions/containers.xml.in +++ b/op-mode-definitions/container.xml.in @@ -11,7 +11,7 @@              <properties>                <help>Pull a new image for container</help>              </properties> -            <command>sudo ${vyos_op_scripts_dir}/containers_op.py --pull "${4}"</command> +            <command>sudo podman image pull "${4}"</command>            </tagNode>          </children>        </node> @@ -44,7 +44,7 @@                  <script>sudo podman image ls -q</script>                </completionHelp>              </properties> -            <command>sudo ${vyos_op_scripts_dir}/containers_op.py --remove "${4}"</command> +            <command>sudo podman image rm --force "${4}"</command>            </tagNode>          </children>        </node> @@ -100,13 +100,13 @@          <properties>            <help>Show containers</help>          </properties> -        <command>sudo ${vyos_op_scripts_dir}/containers_op.py --all</command> +        <command>sudo podman ps --all</command>          <children>            <leafNode name="image">              <properties>                <help>Show container image</help>              </properties> -            <command>sudo ${vyos_op_scripts_dir}/containers_op.py --image</command> +            <command>sudo podman image ls</command>            </leafNode>            <tagNode name="log">              <properties> @@ -121,7 +121,7 @@              <properties>                <help>Show available container networks</help>              </properties> -            <command>sudo ${vyos_op_scripts_dir}/containers_op.py --networks</command> +            <command>sudo podman network ls</command>            </leafNode>          </children>        </node> @@ -167,7 +167,7 @@                  <path>container name</path>                </completionHelp>              </properties> -            <command>sudo ${vyos_op_scripts_dir}/containers_op.py --update "${4}"</command> +            <command>if cli-shell-api existsActive container name "$4"; then sudo podman pull $(cli-shell-api returnActiveValue container name "$4" image); else echo "Container $4 does not exist"; fi</command>            </tagNode>          </children>        </node> diff --git a/op-mode-definitions/monitor-log.xml.in b/op-mode-definitions/monitor-log.xml.in index 6f82ce611..7ecce4f78 100644 --- a/op-mode-definitions/monitor-log.xml.in +++ b/op-mode-definitions/monitor-log.xml.in @@ -16,18 +16,18 @@            </node>            <node name="dhcp">              <properties> -              <help>Show log for Dynamic Host Control Protocol (DHCP)</help> +              <help>Monitor last lines of Dynamic Host Control Protocol (DHCP)</help>              </properties>              <children>                <node name="server">                  <properties> -                  <help>Show log for DHCP server</help> +                  <help>Monitor last lines of DHCP server</help>                  </properties>                  <command>journalctl --no-hostname --follow --boot --unit isc-dhcp-server.service</command>                </node>                <node name="client">                  <properties> -                  <help>Show DHCP client logs</help> +                  <help>Monitor last lines of DHCP client</help>                  </properties>                  <command>journalctl --no-hostname --follow --boot --unit "dhclient@*.service"</command>                  <children> @@ -46,18 +46,18 @@            </node>            <node name="dhcpv6">              <properties> -              <help>Show log for Dynamic Host Control Protocol IPv6 (DHCPv6)</help> +              <help>Monitor last lines of Dynamic Host Control Protocol IPv6 (DHCPv6)</help>              </properties>              <children>                <node name="server">                  <properties> -                  <help>Show log for DHCPv6 server</help> +                  <help>Monitor last lines of DHCPv6 server</help>                  </properties>                  <command>journalctl --no-hostname --follow --boot --unit isc-dhcp-server6.service</command>                </node>                <node name="client">                  <properties> -                  <help>Show DHCPv6 client logs</help> +                  <help>Monitor last lines of DHCPv6 client</help>                  </properties>                  <command>journalctl --no-hostname --follow --boot --unit "dhcp6c@*.service"</command>                  <children> @@ -74,12 +74,24 @@                </node>              </children>            </node> +          <leafNode name="flow-accounting"> +            <properties> +              <help>Monitor last lines of flow-accounting log</help> +            </properties> +            <command>journalctl --no-hostname --boot --follow --unit uacctd.service</command> +          </leafNode>            <leafNode name="kernel">              <properties>                <help>Monitor last lines of Linux Kernel log</help>              </properties>              <command>journalctl --no-hostname --boot --follow --dmesg</command>            </leafNode> +          <leafNode name="nhrp"> +            <properties> +              <help>Monitor last lines of NHRP log</help> +            </properties> +            <command>journalctl --no-hostname --boot --unit opennhrp.service</command> +          </leafNode>            <node name="pppoe">              <properties>                <help>Monitor last lines of PPPoE log</help> diff --git a/op-mode-definitions/show-log.xml.in b/op-mode-definitions/show-log.xml.in index 954369712..76879e5d6 100644 --- a/op-mode-definitions/show-log.xml.in +++ b/op-mode-definitions/show-log.xml.in @@ -139,6 +139,12 @@                </tagNode>              </children>            </node> +          <leafNode name="flow-accounting"> +            <properties> +              <help>Show log for flow-accounting</help> +            </properties> +            <command>journalctl --no-hostname --boot --unit uacctd.service</command> +          </leafNode>            <leafNode name="https">              <properties>                <help>Show log for HTTPs</help> @@ -195,6 +201,12 @@              </properties>              <command>egrep -i "kernel:.*\[NAT-[A-Z]{3,}-[0-9]+(-MASQ)?\]" $(find /var/log -maxdepth 1 -type f -name messages\* | sort -t. -k2nr)</command>            </leafNode> +          <leafNode name="nhrp"> +            <properties> +              <help>Show log for NHRP</help> +            </properties> +            <command>journalctl --no-hostname --boot --unit opennhrp.service</command> +          </leafNode>            <node name="openvpn">              <properties>                <help>Show log for OpenVPN</help> diff --git a/op-mode-definitions/show-system.xml.in b/op-mode-definitions/show-system.xml.in index 0f852164e..68b473bc1 100644 --- a/op-mode-definitions/show-system.xml.in +++ b/op-mode-definitions/show-system.xml.in @@ -166,9 +166,9 @@            </leafNode>            <leafNode name="uptime">              <properties> -              <help>Show how long the system has been up</help> +              <help>Show system uptime and load averages</help>              </properties> -            <command>uptime</command> +            <command>${vyos_op_scripts_dir}/show_uptime.py</command>            </leafNode>          </children>        </node> diff --git a/op-mode-definitions/traceroute.xml.in b/op-mode-definitions/traceroute.xml.in index e3217235c..aba0f45e3 100644 --- a/op-mode-definitions/traceroute.xml.in +++ b/op-mode-definitions/traceroute.xml.in @@ -2,226 +2,22 @@  <interfaceDefinition>    <tagNode name="traceroute">      <properties> -      <help>Track network path to node</help> -      <completionHelp> -        <list><hostname> <x.x.x.x> <h:h:h:h:h:h:h:h></list> -      </completionHelp> -    </properties> -    <command>/usr/bin/traceroute "$2"</command> -  </tagNode> -  <node name="traceroute"> -    <properties> -      <help>Track network path to node</help> +      <help>Trace network path to node</help>        <completionHelp>          <list><hostname> <x.x.x.x> <h:h:h:h:h:h:h:h></list>        </completionHelp>      </properties> +    <command>${vyos_op_scripts_dir}/traceroute.py ${@:2}</command>      <children> -      <tagNode name="ipv4"> +      <leafNode name="node.tag">          <properties> -          <help>Explicitly use IPv4 when tracing the path</help> +          <help>Traceroute options</help>            <completionHelp> -            <list><hostname> <x.x.x.x></list> +            <script>${vyos_op_scripts_dir}/traceroute.py --get-options "${COMP_WORDS[@]}"</script>            </completionHelp>          </properties> -        <command>/usr/bin/traceroute -4 "$3"</command> -        <children> -          <node name="tcp"> -            <properties> -              <help>Route tracing and port detection using TCP</help> -            </properties> -            <command>sudo /usr/bin/tcptraceroute "$3" </command> -            <children> -              <tagNode name="port"> -                <properties> -                  <help>TCP port to connect to for path tracing</help> -                  <completionHelp> -                    <list>0-65535</list> -                  </completionHelp> -                </properties> -                <command>sudo /usr/bin/tcptraceroute "$3" $6</command> -              </tagNode> -            </children> -          </node> -        </children> -      </tagNode> -      <tagNode name="ipv6"> -        <properties> -          <help>Explicitly use IPv6 when tracing the path</help> -          <completionHelp> -            <list><hostname> <h:h:h:h:h:h:h:h></list> -          </completionHelp> -        </properties> -        <command>/usr/bin/traceroute -6 "$3"</command> -        <children> -          <node name="tcp"> -            <properties> -              <help>Use TCP/IPv6 packets to perform a traceroute</help> -            </properties> -            <command>sudo /usr/bin/tcptraceroute6 "$3" </command> -            <children> -              <tagNode name="port"> -                <properties> -                  <help>TCP port to connect to for path tracing</help> -                  <completionHelp> -                    <list>0-65535</list> -                  </completionHelp> -                </properties> -                <command>sudo /usr/bin/tcptraceroute6 "$3" $6</command> -              </tagNode> -            </children> -          </node> -        </children> -      </tagNode> -      <tagNode name="vrf"> -        <properties> -          <help>Track network path to specified node via given VRF</help> -          <completionHelp> -            <path>vrf name</path> -          </completionHelp> -        </properties> -        <children> -          <!-- we need an empty tagNode to pass in a plain fqdn/ip address and -               let traceroute decide how to handle this parameter --> -          <tagNode name=""> -            <properties> -              <help>Track network path to specified node via given VRF</help> -              <completionHelp> -                <list><hostname> <x.x.x.x> <h:h:h:h:h:h:h:h></list> -              </completionHelp> -            </properties> -            <command>sudo ip vrf exec "$3" /usr/bin/traceroute "$4"</command> -          </tagNode> -          <tagNode name="ipv4"> -            <properties> -              <help>Explicitly use IPv4 when tracing the path via given VRF</help> -              <completionHelp> -                <list><hostname> <x.x.x.x></list> -              </completionHelp> -            </properties> -            <command>sudo ip vrf exec "$3" /usr/bin/traceroute -4 "$5"</command> -            <children> -              <node name="tcp"> -                <properties> -                  <help>Route tracing and port detection using TCP</help> -                </properties> -                <command>sudo ip vrf exec "$3" /usr/bin/tcptraceroute "$5" </command> -                <children> -                  <tagNode name="port"> -                    <properties> -                      <help>TCP port to connect to for path tracing</help> -                      <completionHelp> -                        <list>0-65535</list> -                      </completionHelp> -                    </properties> -                    <command>sudo ip vrf exec "$3" /usr/bin/tcptraceroute "$5" $8</command> -                  </tagNode> -                </children> -              </node> -            </children> -          </tagNode> -          <tagNode name="ipv6"> -            <properties> -              <help>Explicitly use IPv6 when tracing the path via given VRF</help> -              <completionHelp> -                <list><hostname> <h:h:h:h:h:h:h:h></list> -              </completionHelp> -            </properties> -            <command>sudo ip vrf exec "$3" /usr/bin/traceroute -6 "$5"</command> -            <children> -              <node name="tcp"> -                <properties> -                  <help>Use TCP/IPv6 packets to perform a traceroute</help> -                </properties> -                <command>sudo ip vrf exec "$3" /usr/bin/tcptraceroute6 "$5" </command> -                <children> -                  <tagNode name="port"> -                    <properties> -                      <help>TCP port to connect to for path tracing</help> -                      <completionHelp> -                        <list>0-65535</list> -                      </completionHelp> -                    </properties> -                    <command>sudo ip vrf exec "$3" /usr/bin/tcptraceroute6 "$5" $8</command> -                  </tagNode> -                </children> -              </node> -            </children> -          </tagNode> -        </children> -      </tagNode> -    </children> -  </node> -  <node name="monitor"> -    <children> -      <tagNode name="traceroute"> -        <properties> -          <help>Monitor path to destination in realtime</help> -          <completionHelp> -            <list><hostname> <x.x.x.x> <h:h:h:h:h:h:h:h></list> -          </completionHelp> -        </properties> -        <command>/usr/bin/mtr "$3"</command> -      </tagNode> -      <node name="traceroute"> -        <children> -          <tagNode name="ipv4"> -            <properties> -              <help>IPv4 fully qualified domain name (FQDN)</help> -              <completionHelp> -                <list><fqdn></list> -              </completionHelp> -            </properties> -            <command>/usr/bin/mtr -4 "$4"</command> -          </tagNode> -          <tagNode name="ipv6"> -            <properties> -              <help>IPv6 fully qualified domain name (FQDN)</help> -              <completionHelp> -                <list><fqdn></list> -              </completionHelp> -            </properties> -            <command>/usr/bin/mtr -6 "$4"</command> -          </tagNode> -          <tagNode name="vrf"> -            <properties> -              <help>Monitor path to destination in realtime via given VRF</help> -              <completionHelp> -                <path>vrf name</path> -              </completionHelp> -            </properties> -            <children> -              <tagNode name="ipv4"> -                <properties> -                  <help>IPv4 fully qualified domain name (FQDN)</help> -                  <completionHelp> -                    <list><fqdn></list> -                  </completionHelp> -                </properties> -                <command>sudo ip vrf exec "$4" /usr/bin/mtr -4 "$6"</command> -              </tagNode> -              <tagNode name="ipv6"> -                <properties> -                  <help>IPv6 fully qualified domain name (FQDN)</help> -                  <completionHelp> -                    <list><fqdn></list> -                  </completionHelp> -                </properties> -                <command>sudo ip vrf exec "$4" /usr/bin/mtr -6 "$6"</command> -              </tagNode> -              <tagNode name=""> -                <properties> -                  <help>Track network path to specified node via given VRF</help> -                  <completionHelp> -                    <list><hostname> <x.x.x.x> <h:h:h:h:h:h:h:h></list> -                  </completionHelp> -                </properties> -                <command>sudo ip vrf exec "$4" /usr/bin/mtr "$5"</command> -              </tagNode> -            </children> -          </tagNode> -        </children> -      </node> +        <command>${vyos_op_scripts_dir}/traceroute.py ${@:2}</command> +      </leafNode>      </children> -  </node> +  </tagNode>  </interfaceDefinition> diff --git a/python/vyos/cpu.py b/python/vyos/cpu.py new file mode 100644 index 000000000..a0ef864be --- /dev/null +++ b/python/vyos/cpu.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +# Copyright 2022 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library.  If not, see <http://www.gnu.org/licenses/>. + +""" +Retrieves (or at least attempts to retrieve) the total number of real CPU cores +installed in a Linux system. + +The issue of core count is complicated by existence of SMT, e.g. Intel's Hyper Threading. +GNU nproc returns the number of LOGICAL cores, +which is 2x of the real cores if SMT is enabled. + +The idea is to find all physical CPUs and add up their core counts. +It has special cases for x86_64 and MAY work correctly on other architectures, +but nothing is certain. +""" + +import re + + +def _read_cpuinfo(): +    with open('/proc/cpuinfo', 'r') as f: +        return f.readlines() + +def _split_line(l): +    l = l.strip() +    parts = re.split(r'\s*:\s*', l) +    return (parts[0], ":".join(parts[1:])) + +def _find_cpus(cpuinfo_lines): +    # Make a dict because it's more convenient to work with later, +    # when we need to find physicall distinct CPUs there. +    cpus = {} + +    cpu_number = 0 + +    for l in cpuinfo_lines: +        key, value = _split_line(l) +        if key == 'processor': +            cpu_number = value +            cpus[cpu_number] = {} +        else: +            cpus[cpu_number][key] = value + +    return cpus + +def _find_physical_cpus(): +    cpus = _find_cpus(_read_cpuinfo()) + +    phys_cpus = {} + +    for num in cpus: +        if 'physical id' in cpus[num]: +            # On at least some architectures, CPUs in different sockets +            # have different 'physical id' field, e.g. on x86_64. +            phys_id = cpus[num]['physical id'] +            if phys_id not in phys_cpus: +                phys_cpus[phys_id] = cpus[num] +        else: +            # On other architectures, e.g. on ARM, there's no such field. +            # We just assume they are different CPUs, +            # whether single core ones or cores of physical CPUs. +            phys_cpus[num] = cpu[num] + +    return phys_cpus + +def get_cpus(): +    """ Returns a list of /proc/cpuinfo entries that belong to different CPUs. +    """ +    cpus_dict = _find_physical_cpus() +    return list(cpus_dict.values()) + +def get_core_count(): +    """ Returns the total number of physical CPU cores +        (even if Hyper-Threading or another SMT is enabled and has inflated +        the number of cores in /proc/cpuinfo) +    """ +    physical_cpus = _find_physical_cpus() + +    core_count = 0 + +    for num in physical_cpus: +        # Some architectures, e.g. x86_64, include a field for core count. +        # Since we found unique physical CPU entries, we can sum their core counts. +        if 'cpu cores' in physical_cpus[num]: +            core_count += int(physical_cpus[num]['cpu cores']) +        else: +            core_count += 1 + +    return core_count diff --git a/python/vyos/firewall.py b/python/vyos/firewall.py index ff8623592..04fd44173 100644 --- a/python/vyos/firewall.py +++ b/python/vyos/firewall.py @@ -49,6 +49,15 @@ def parse_rule(rule_conf, fw_name, rule_id, ip_name):          if states:              output.append(f'ct state {{{states}}}') +    if 'connection_status' in rule_conf and rule_conf['connection_status']: +        status = rule_conf['connection_status'] +        if status['nat'] == 'destination': +            nat_status = '{dnat}' +            output.append(f'ct status {nat_status}') +        if status['nat'] == 'source': +            nat_status = '{snat}' +            output.append(f'ct status {nat_status}') +      if 'protocol' in rule_conf and rule_conf['protocol'] != 'all':          proto = rule_conf['protocol']          operator = '' diff --git a/smoketest/configs/firewall-big b/smoketest/configs.no-load/firewall-big index 94b0c6dd5..94b0c6dd5 100644 --- a/smoketest/configs/firewall-big +++ b/smoketest/configs.no-load/firewall-big diff --git a/smoketest/configs/bgp-big-as-cloud b/smoketest/configs/bgp-big-as-cloud index 10660ec87..65819256e 100644 --- a/smoketest/configs/bgp-big-as-cloud +++ b/smoketest/configs/bgp-big-as-cloud @@ -982,6 +982,10 @@ policy {                      }                  }              } +            set { +                as-path-exclude "100 200 300" +                as-path-prepend "64512 64512 64512" +            }          }          rule 100 {              action deny diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py index 37e6b9e7a..b8f944575 100755 --- a/smoketest/scripts/cli/test_firewall.py +++ b/smoketest/scripts/cli/test_firewall.py @@ -173,6 +173,45 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):                  nftables_output = cmd(f'sudo nft list chain {table} {chain}')                  self.assertTrue('jump VYOS_STATE_POLICY' in nftables_output) +    def test_state_and_status_rules(self): +        self.cli_set(['firewall', 'name', 'smoketest', 'default-action', 'drop']) +        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'action', 'accept']) +        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'state', 'established', 'enable']) +        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '1', 'state', 'related', 'enable']) +        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'action', 'reject']) +        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '2', 'state', 'invalid', 'enable']) +        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '3', 'action', 'accept']) +        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '3', 'state', 'new', 'enable']) + +        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '3', 'connection-status', 'nat', 'destination']) +        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '4', 'action', 'accept']) +        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '4', 'state', 'new', 'enable']) +        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '4', 'state', 'established', 'enable']) +        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '4', 'connection-status', 'nat', 'source']) + +        self.cli_set(['interfaces', 'ethernet', 'eth0', 'firewall', 'in', 'name', 'smoketest']) + +        self.cli_commit() + +        nftables_search = [ +            ['iifname "eth0"', 'jump NAME_smoketest'], +            ['ct state { established, related }', 'return'], +            ['ct state { invalid }', 'reject'], +            ['ct state { new }', 'ct status { dnat }', 'return'], +            ['ct state { established, new }', 'ct status { snat }', 'return'], +            ['smoketest default-action', 'drop'] +        ] + +        nftables_output = cmd('sudo nft list table ip filter') + +        for search in nftables_search: +            matched = False +            for line in nftables_output.split("\n"): +                if all(item in line for item in search): +                    matched = True +                    break +            self.assertTrue(matched, msg=search) +      def test_sysfs(self):          for name, conf in sysfs_config.items():              paths = glob(conf['sysfs']) diff --git a/smoketest/scripts/cli/test_policy.py b/smoketest/scripts/cli/test_policy.py index b232a2241..e8c6ff19b 100755 --- a/smoketest/scripts/cli/test_policy.py +++ b/smoketest/scripts/cli/test_policy.py @@ -1,6 +1,6 @@  #!/usr/bin/env python3  # -# Copyright (C) 2021 VyOS maintainers and contributors +# Copyright (C) 2021-2022 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 @@ -800,27 +800,28 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):                      '10' : {                          'action' : 'deny',                          'set' : { -                            'aggregator-as'       : '1234567890', -                            'aggregator-ip'       : '10.255.255.0', -                            'as-path-exclude'     : '1234', -                            'as-path-prepend'     : '1234567890 987654321', -                            'atomic-aggregate'    : '', -                            'distance'            : '110', -                            'extcommunity-bw'     : '20000', -                            'extcommunity-rt'     : '123:456', -                            'extcommunity-soo'    : '456:789', -                            'ipv6-next-hop-global': '2001::1', -                            'ipv6-next-hop-local' : 'fe80::1', -                            'ip-next-hop'         : '192.168.1.1', -                            'large-community'     : '100:200:300', -                            'local-preference'    : '500', -                            'metric'              : '150', -                            'metric-type'         : 'type-1', -                            'origin'              : 'incomplete', -                            'originator-id'       : '172.16.10.1', -                            'src'                 : '100.0.0.1', -                            'tag'                 : '65530', -                            'weight'              : '2', +                            'aggregator-as'           : '1234567890', +                            'aggregator-ip'           : '10.255.255.0', +                            'as-path-exclude'         : '1234', +                            'as-path-prepend'         : '1234567890 987654321', +                            'as-path-prepend-last-as' : '5', +                            'atomic-aggregate'        : '', +                            'distance'                : '110', +                            'extcommunity-bw'         : '20000', +                            'extcommunity-rt'         : '123:456', +                            'extcommunity-soo'        : '456:789', +                            'ipv6-next-hop-global'    : '2001::1', +                            'ipv6-next-hop-local'     : 'fe80::1', +                            'ip-next-hop'             : '192.168.1.1', +                            'large-community'         : '100:200:300', +                            'local-preference'        : '500', +                            'metric'                  : '150', +                            'metric-type'             : 'type-1', +                            'origin'                  : 'incomplete', +                            'originator-id'           : '172.16.10.1', +                            'src'                     : '100.0.0.1', +                            'tag'                     : '65530', +                            'weight'                  : '2',                          },                      },                  }, @@ -848,6 +849,13 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):                              'evpn-vni'            : '1234',                          },                      }, +                    '20' : { +                        'action' : 'permit', +                        'set' : { +                            'evpn-gateway-ipv4'   : '192.0.2.99', +                            'evpn-gateway-ipv6'   : '2001:db8:f00::1', +                        }, +                    },                  },              },          } @@ -958,9 +966,9 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):                      if 'aggregator-ip' in rule_config['set']:                          self.cli_set(path + ['rule', rule, 'set', 'aggregator', 'ip', rule_config['set']['aggregator-ip']])                      if 'as-path-exclude' in rule_config['set']: -                        self.cli_set(path + ['rule', rule, 'set', 'as-path-exclude', rule_config['set']['as-path-exclude']]) +                        self.cli_set(path + ['rule', rule, 'set', 'as-path', 'exclude', rule_config['set']['as-path-exclude']])                      if 'as-path-prepend' in rule_config['set']: -                        self.cli_set(path + ['rule', rule, 'set', 'as-path-prepend', rule_config['set']['as-path-prepend']]) +                        self.cli_set(path + ['rule', rule, 'set', 'as-path', 'prepend', rule_config['set']['as-path-prepend']])                      if 'atomic-aggregate' in rule_config['set']:                          self.cli_set(path + ['rule', rule, 'set', 'atomic-aggregate'])                      if 'distance' in rule_config['set']: @@ -995,6 +1003,10 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):                          self.cli_set(path + ['rule', rule, 'set', 'tag', rule_config['set']['tag']])                      if 'weight' in rule_config['set']:                          self.cli_set(path + ['rule', rule, 'set', 'weight', rule_config['set']['weight']]) +                    if 'evpn-gateway-ipv4' in rule_config['set']: +                        self.cli_set(path + ['rule', rule, 'set', 'evpn', 'gateway', 'ipv4', rule_config['set']['evpn-gateway-ipv4']]) +                    if 'evpn-gateway-ipv6' in rule_config['set']: +                        self.cli_set(path + ['rule', rule, 'set', 'evpn', 'gateway', 'ipv6', rule_config['set']['evpn-gateway-ipv6']])          self.cli_commit() @@ -1118,6 +1130,8 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):                          tmp += 'as-path exclude ' + rule_config['set']['as-path-exclude']                      elif 'as-path-prepend' in rule_config['set']:                          tmp += 'as-path prepend ' + rule_config['set']['as-path-prepend'] +                    elif 'as-path-prepend-last-as' in rule_config['set']: +                        tmp += 'as-path prepend last-as' + rule_config['set']['as-path-prepend-last-as']                      elif 'atomic-aggregate' in rule_config['set']:                          tmp += 'atomic-aggregate'                      elif 'distance' in rule_config['set']: @@ -1152,6 +1166,10 @@ class TestPolicy(VyOSUnitTestSHIM.TestCase):                          tmp += 'tag ' + rule_config['set']['tag']                      elif 'weight' in rule_config['set']:                          tmp += 'weight ' + rule_config['set']['weight'] +                    elif 'vpn-gateway-ipv4' in rule_config['set']: +                        tmp += 'evpn gateway ipv4 ' + rule_config['set']['vpn-gateway-ipv4'] +                    elif 'vpn-gateway-ipv6' in rule_config['set']: +                        tmp += 'evpn gateway ipv6 ' + rule_config['set']['vpn-gateway-ipv6']                      self.assertIn(tmp, config) diff --git a/smoketest/scripts/cli/test_policy_route.py b/smoketest/scripts/cli/test_policy_route.py index 9035f0832..e2d70f289 100755 --- a/smoketest/scripts/cli/test_policy_route.py +++ b/smoketest/scripts/cli/test_policy_route.py @@ -1,6 +1,6 @@  #!/usr/bin/env python3  # -# Copyright (C) 2021 VyOS maintainers and contributors +# Copyright (C) 2021-2022 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 @@ -23,15 +23,26 @@ from vyos.util import cmd  mark = '100'  table_mark_offset = 0x7fffffff  table_id = '101' +interface = 'eth0' +interface_ip = '172.16.10.1/24'  class TestPolicyRoute(VyOSUnitTestSHIM.TestCase): -    def setUp(self): -        self.cli_set(['interfaces', 'ethernet', 'eth0', 'address', '172.16.10.1/24']) -        self.cli_set(['protocols', 'static', 'table', '101', 'route', '0.0.0.0/0', 'interface', 'eth0']) +    @classmethod +    def setUpClass(cls): +        super(TestPolicyRoute, cls).setUpClass() + +        cls.cli_set(cls, ['interfaces', 'ethernet', interface, 'address', interface_ip]) +        cls.cli_set(cls, ['protocols', 'static', 'table', table_id, 'route', '0.0.0.0/0', 'interface', interface]) + +    @classmethod +    def tearDownClass(cls): +        cls.cli_delete(cls, ['interfaces', 'ethernet', interface, 'address', interface_ip]) +        cls.cli_delete(cls, ['protocols', 'static', 'table', table_id]) + +        super(TestPolicyRoute, cls).tearDownClass()      def tearDown(self): -        self.cli_delete(['interfaces', 'ethernet', 'eth0']) -        self.cli_delete(['protocols', 'static']) +        self.cli_delete(['interfaces', 'ethernet', interface, 'policy'])          self.cli_delete(['policy', 'route'])          self.cli_delete(['policy', 'route6'])          self.cli_commit() @@ -41,14 +52,14 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase):          self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'destination', 'address', '172.16.10.10'])          self.cli_set(['policy', 'route', 'smoketest', 'rule', '1', 'set', 'mark', mark]) -        self.cli_set(['interfaces', 'ethernet', 'eth0', 'policy', 'route', 'smoketest']) +        self.cli_set(['interfaces', 'ethernet', interface, 'policy', 'route', 'smoketest'])          self.cli_commit()          mark_hex = "{0:#010x}".format(int(mark))          nftables_search = [ -            ['iifname "eth0"', 'jump VYOS_PBR_smoketest'], +            [f'iifname "{interface}"','jump VYOS_PBR_smoketest'],              ['ip daddr 172.16.10.10', 'ip saddr 172.16.20.10', 'meta mark set ' + mark_hex],          ] @@ -72,8 +83,8 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase):          self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '1', 'destination', 'port', '8888'])          self.cli_set(['policy', 'route6', 'smoketest6', 'rule', '1', 'set', 'table', table_id]) -        self.cli_set(['interfaces', 'ethernet', 'eth0', 'policy', 'route', 'smoketest']) -        self.cli_set(['interfaces', 'ethernet', 'eth0', 'policy', 'route6', 'smoketest6']) +        self.cli_set(['interfaces', 'ethernet', interface, 'policy', 'route', 'smoketest']) +        self.cli_set(['interfaces', 'ethernet', interface, 'policy', 'route6', 'smoketest6'])          self.cli_commit() @@ -82,7 +93,7 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase):          # IPv4          nftables_search = [ -            ['iifname "eth0"', 'jump VYOS_PBR_smoketest'], +            [f'iifname "{interface}"', 'jump VYOS_PBR_smoketest'],              ['tcp flags & (syn | ack) == syn', 'tcp dport { 8888 }', 'meta mark set ' + mark_hex]          ] @@ -99,7 +110,7 @@ class TestPolicyRoute(VyOSUnitTestSHIM.TestCase):          # IPv6          nftables6_search = [ -            ['iifname "eth0"', 'jump VYOS_PBR6_smoketest'], +            [f'iifname "{interface}"', 'jump VYOS_PBR6_smoketest'],              ['meta l4proto { tcp, udp }', 'th dport { 8888 }', 'meta mark set ' + mark_hex]          ] diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py index 6f92457b2..9c0c93779 100755 --- a/smoketest/scripts/cli/test_protocols_bgp.py +++ b/smoketest/scripts/cli/test_protocols_bgp.py @@ -887,6 +887,7 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):          remote_asn = str(int(ASN) + 150)          neighbor = '192.0.2.1'          peer_group = 'bar' +        interface = 'eth0'          self.cli_set(base_path + ['local-as', ASN])          self.cli_set(base_path + ['neighbor', neighbor, 'remote-as', remote_asn]) @@ -898,6 +899,20 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):              self.cli_commit()          self.cli_delete(base_path + ['neighbor', neighbor, 'remote-as']) +        # re-test with interface based peer-group +        self.cli_set(base_path + ['neighbor', interface, 'interface', 'peer-group', peer_group]) +        self.cli_set(base_path + ['neighbor', interface, 'interface', 'remote-as', 'external']) +        with self.assertRaises(ConfigSessionError): +            self.cli_commit() +        self.cli_delete(base_path + ['neighbor', interface, 'interface', 'remote-as']) + +        # re-test with interface based v6only peer-group +        self.cli_set(base_path + ['neighbor', interface, 'interface', 'v6only', 'peer-group', peer_group]) +        self.cli_set(base_path + ['neighbor', interface, 'interface', 'v6only', 'remote-as', 'external']) +        with self.assertRaises(ConfigSessionError): +            self.cli_commit() +        self.cli_delete(base_path + ['neighbor', interface, 'interface', 'v6only', 'remote-as']) +          self.cli_commit()          frrconfig = self.getFRRconfig(f'router bgp {ASN}') diff --git a/smoketest/scripts/cli/test_service_ssh.py b/smoketest/scripts/cli/test_service_ssh.py index 77ad5bc0d..0b029dd00 100755 --- a/smoketest/scripts/cli/test_service_ssh.py +++ b/smoketest/scripts/cli/test_service_ssh.py @@ -213,5 +213,54 @@ class TestServiceSSH(VyOSUnitTestSHIM.TestCase):          usernames = [x[0] for x in getpwall()]          self.assertNotIn(test_user, usernames) +    def test_ssh_dynamic_protection(self): +        # check sshguard service + +        SSHGUARD_CONFIG = '/etc/sshguard/sshguard.conf' +        SSHGUARD_WHITELIST = '/etc/sshguard/whitelist' +        SSHGUARD_PROCESS = 'sshguard' +        block_time = '123' +        detect_time = '1804' +        port = '22' +        threshold = '10' +        allow_list = ['192.0.2.0/24', '2001:db8::/48'] + +        self.cli_set(base_path + ['dynamic-protection', 'block-time', block_time]) +        self.cli_set(base_path + ['dynamic-protection', 'detect-time', detect_time]) +        self.cli_set(base_path + ['dynamic-protection', 'threshold', threshold]) +        for allow in allow_list: +            self.cli_set(base_path + ['dynamic-protection', 'allow-from', allow]) + +        # commit changes +        self.cli_commit() + +        # Check configured port +        tmp = get_config_value('Port') +        self.assertIn(port, tmp) + +        # Check sshgurad service +        self.assertTrue(process_named_running(SSHGUARD_PROCESS)) + +        sshguard_lines = [ +            f'THRESHOLD={threshold}', +            f'BLOCK_TIME={block_time}', +            f'DETECTION_TIME={detect_time}' +        ] + +        tmp_sshguard_conf = read_file(SSHGUARD_CONFIG) +        for line in sshguard_lines: +            self.assertIn(line, tmp_sshguard_conf) + +        tmp_whitelist_conf = read_file(SSHGUARD_WHITELIST) +        for allow in allow_list: +            self.assertIn(allow, tmp_whitelist_conf) + +        # Delete service ssh dynamic-protection +        # but not service ssh itself +        self.cli_delete(base_path + ['dynamic-protection']) +        self.cli_commit() + +        self.assertFalse(process_named_running(SSHGUARD_PROCESS)) +  if __name__ == '__main__':      unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_system_flow-accounting.py b/smoketest/scripts/cli/test_system_flow-accounting.py index 5a73ebc7d..a6eef3fb6 100755 --- a/smoketest/scripts/cli/test_system_flow-accounting.py +++ b/smoketest/scripts/cli/test_system_flow-accounting.py @@ -1,6 +1,6 @@  #!/usr/bin/env python3  # -# Copyright (C) 2021 VyOS maintainers and contributors +# Copyright (C) 2021-2022 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 @@ -20,6 +20,8 @@ from base_vyostest_shim import VyOSUnitTestSHIM  from vyos.configsession import ConfigSessionError  from vyos.ifconfig import Section +from vyos.template import bracketize_ipv6 +from vyos.template import is_ipv6  from vyos.util import cmd  from vyos.util import process_named_running  from vyos.util import read_file @@ -103,14 +105,12 @@ class TestSystemFlowAccounting(VyOSUnitTestSHIM.TestCase):          agent_address = '192.0.2.2'          sflow_server = { -            '1.2.3.4' : { -            }, -            '5.6.7.8' : { -                'port' : '6000' -            } +            '1.2.3.4' : { }, +            '5.6.7.8' : { 'port' : '6000' },          }          self.cli_set(['interfaces', 'dummy', dummy_if, 'address', agent_address + '/32']) +        self.cli_set(['interfaces', 'dummy', dummy_if, 'address', source_address + '/32'])          self.cli_set(base_path + ['disable-imt'])          # You need to configure at least one interface for flow-accounting @@ -155,6 +155,54 @@ class TestSystemFlowAccounting(VyOSUnitTestSHIM.TestCase):          self.cli_delete(['interfaces', 'dummy', dummy_if]) +    def test_sflow_ipv6(self): +        sampling_rate = '100' +        sflow_server = { +            '2001:db8::1' : { }, +            '2001:db8::2' : { 'port' : '6000' }, +        } + +        self.cli_set(base_path + ['disable-imt']) + +        # You need to configure at least one interface for flow-accounting +        with self.assertRaises(ConfigSessionError): +            self.cli_commit() +        for interface in Section.interfaces('ethernet'): +            self.cli_set(base_path + ['interface', interface]) + + +        # You need to configure at least one sFlow or NetFlow protocol, or not +        # set "disable-imt" for flow-accounting +        with self.assertRaises(ConfigSessionError): +            self.cli_commit() + +        self.cli_set(base_path + ['sflow', 'sampling-rate', sampling_rate]) +        for server, server_config in sflow_server.items(): +            self.cli_set(base_path + ['sflow', 'server', server]) +            if 'port' in server_config: +                self.cli_set(base_path + ['sflow', 'server', server, 'port', server_config['port']]) + +        # commit changes +        self.cli_commit() + +        uacctd = read_file(uacctd_conf) + +        # when 'disable-imt' is not configured on the CLI it must be present +        self.assertNotIn(f'imt_path: /tmp/uacctd.pipe', uacctd) +        self.assertNotIn(f'imt_mem_pools_number: 169', uacctd) +        self.assertNotIn(f'plugins: memory', uacctd) + +        for server, server_config in sflow_server.items(): +            tmp_srv = server +            if is_ipv6(tmp_srv): +                tmp_srv = tmp_srv.replace(':', '.') + +            if 'port' in server_config: +                self.assertIn(f'sfprobe_receiver[sf_{tmp_srv}]: {bracketize_ipv6(server)}', uacctd) +            else: +                self.assertIn(f'sfprobe_receiver[sf_{tmp_srv}]: {bracketize_ipv6(server)}:6343', uacctd) +            self.assertIn(f'sampling_rate[sf_{tmp_srv}]: {sampling_rate}', uacctd) +      def test_netflow(self):          engine_id = '33'          max_flows = '667' @@ -173,14 +221,13 @@ class TestSystemFlowAccounting(VyOSUnitTestSHIM.TestCase):          tmo_udp = '10'          netflow_server = { -            '11.22.33.44' : { -            }, -            '55.66.77.88' : { -                'port' : '6000' -            } +            '11.22.33.44' : { }, +            '55.66.77.88' : { 'port' : '6000' }, +            '2001:db8::1' : { },          }          self.cli_set(['interfaces', 'dummy', dummy_if, 'address', agent_address + '/32']) +        self.cli_set(['interfaces', 'dummy', dummy_if, 'address', source_address + '/32'])          for interface in Section.interfaces('ethernet'):              self.cli_set(base_path + ['interface', interface]) @@ -217,23 +264,30 @@ class TestSystemFlowAccounting(VyOSUnitTestSHIM.TestCase):          tmp = []          for server, server_config in netflow_server.items(): -            tmp.append(f'nfprobe[nf_{server}]') +            tmp_srv = server +            if is_ipv6(tmp_srv): +                tmp_srv = tmp_srv.replace(':', '.') +            tmp.append(f'nfprobe[nf_{tmp_srv}]')          tmp.append('memory')          self.assertIn('plugins: ' + ','.join(tmp), uacctd)          for server, server_config in netflow_server.items(): -            self.assertIn(f'nfprobe_engine[nf_{server}]: {engine_id}', uacctd) -            self.assertIn(f'nfprobe_maxflows[nf_{server}]: {max_flows}', uacctd) -            self.assertIn(f'sampling_rate[nf_{server}]: {sampling_rate}', uacctd) -            self.assertIn(f'nfprobe_source_ip[nf_{server}]: {source_address}', uacctd) -            self.assertIn(f'nfprobe_version[nf_{server}]: {version}', uacctd) +            tmp_srv = server +            if is_ipv6(tmp_srv): +                tmp_srv = tmp_srv.replace(':', '.') + +            self.assertIn(f'nfprobe_engine[nf_{tmp_srv}]: {engine_id}', uacctd) +            self.assertIn(f'nfprobe_maxflows[nf_{tmp_srv}]: {max_flows}', uacctd) +            self.assertIn(f'sampling_rate[nf_{tmp_srv}]: {sampling_rate}', uacctd) +            self.assertIn(f'nfprobe_source_ip[nf_{tmp_srv}]: {source_address}', uacctd) +            self.assertIn(f'nfprobe_version[nf_{tmp_srv}]: {version}', uacctd)              if 'port' in server_config: -                self.assertIn(f'nfprobe_receiver[nf_{server}]: {server}', uacctd) +                self.assertIn(f'nfprobe_receiver[nf_{tmp_srv}]: {bracketize_ipv6(server)}', uacctd)              else: -                self.assertIn(f'nfprobe_receiver[nf_{server}]: {server}:2055', uacctd) +                self.assertIn(f'nfprobe_receiver[nf_{tmp_srv}]: {bracketize_ipv6(server)}:2055', uacctd) -            self.assertIn(f'nfprobe_timeouts[nf_{server}]: expint={tmo_expiry}:general={tmo_flow}:icmp={tmo_icmp}:maxlife={tmo_max}:tcp.fin={tmo_tcp_fin}:tcp={tmo_tcp_generic}:tcp.rst={tmo_tcp_rst}:udp={tmo_udp}', uacctd) +            self.assertIn(f'nfprobe_timeouts[nf_{tmp_srv}]: expint={tmo_expiry}:general={tmo_flow}:icmp={tmo_icmp}:maxlife={tmo_max}:tcp.fin={tmo_tcp_fin}:tcp={tmo_tcp_generic}:tcp.rst={tmo_tcp_rst}:udp={tmo_udp}', uacctd)          self.cli_delete(['interfaces', 'dummy', dummy_if]) diff --git a/smoketest/scripts/cli/test_system_frr.py b/smoketest/scripts/cli/test_system_frr.py new file mode 100755 index 000000000..331133ed4 --- /dev/null +++ b/smoketest/scripts/cli/test_system_frr.py @@ -0,0 +1,146 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2019-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 re +import unittest +from base_vyostest_shim import VyOSUnitTestSHIM +from vyos.util import read_file + +config_file = '/etc/frr/daemons' +base_path = ['system', 'frr'] + + +def daemons_config_parse(daemons_config): +    # create regex for parsing daemons options +    regex_daemon_config = re.compile( +        r'^(?P<daemon_name>\w+)_options="(?P<daemon_options>.*)"$', re.M) +    # create empty dict for config +    daemons_config_dict = {} +    # fill dictionary with actual config +    for daemon in regex_daemon_config.finditer(daemons_config): +        daemon_name = daemon.group('daemon_name') +        daemon_options = daemon.group('daemon_options') +        daemons_config_dict[daemon_name] = daemon_options + +    # return daemons config +    return (daemons_config_dict) + + +class TestSystemFRR(VyOSUnitTestSHIM.TestCase): + +    def tearDown(self): +        self.cli_delete(base_path) +        self.cli_commit() + +    def test_frr_snmp_multipledaemons(self): +        # test SNMP integration for multiple daemons +        test_daemon_names = ['ospfd', 'bgpd'] +        for test_daemon_name in test_daemon_names: +            self.cli_set(base_path + ['snmp', test_daemon_name]) +        self.cli_commit() + +        # read the config file and check content +        daemons_config = read_file(config_file) +        daemons_config_dict = daemons_config_parse(daemons_config) +        # prepare regex for matching SNMP integration +        regex_snmp = re.compile(r'^.* -M snmp.*$') +        for (daemon_name, daemon_options) in daemons_config_dict.items(): +            snmp_enabled = regex_snmp.match(daemon_options) +            if daemon_name in test_daemon_names: +                self.assertTrue(snmp_enabled) +            else: +                self.assertFalse(snmp_enabled) + +    def test_frr_snmp_addandremove(self): +        # test enabling and disabling of SNMP integration +        test_daemon_names = ['ospfd', 'bgpd'] +        for test_daemon_name in test_daemon_names: +            self.cli_set(base_path + ['snmp', test_daemon_name]) +        self.cli_commit() + +        self.cli_delete(base_path) +        self.cli_commit() + +        # read the config file and check content +        daemons_config = read_file(config_file) +        daemons_config_dict = daemons_config_parse(daemons_config) +        # prepare regex for matching SNMP integration +        regex_snmp = re.compile(r'^.* -M snmp.*$') +        for test_daemon_name in test_daemon_names: +            snmp_enabled = regex_snmp.match( +                daemons_config_dict[test_daemon_name]) +            self.assertFalse(snmp_enabled) + +    def test_frr_snmp_empty(self): +        # test empty config section +        self.cli_set(base_path + ['snmp']) +        self.cli_commit() + +        # read the config file and check content +        daemons_config = read_file(config_file) +        daemons_config_dict = daemons_config_parse(daemons_config) +        # prepare regex for matching SNMP integration +        regex_snmp = re.compile(r'^.* -M snmp.*$') +        for daemon_options in daemons_config_dict.values(): +            snmp_enabled = regex_snmp.match(daemon_options) +            self.assertFalse(snmp_enabled) + +    def test_frr_bmp(self): +        # test BMP +        self.cli_set(base_path + ['bmp']) +        self.cli_commit() + +        # read the config file and check content +        daemons_config = read_file(config_file) +        daemons_config_dict = daemons_config_parse(daemons_config) +        # prepare regex +        regex_bmp = re.compile(r'^.* -M bmp.*$') +        bmp_enabled = regex_bmp.match(daemons_config_dict['bgpd']) +        self.assertTrue(bmp_enabled) + +    def test_frr_irdp(self): +        # test IRDP +        self.cli_set(base_path + ['irdp']) +        self.cli_commit() + +        # read the config file and check content +        daemons_config = read_file(config_file) +        daemons_config_dict = daemons_config_parse(daemons_config) +        # prepare regex +        regex_irdp = re.compile(r'^.* -M irdp.*$') +        irdp_enabled = regex_irdp.match(daemons_config_dict['zebra']) +        self.assertTrue(irdp_enabled) + +    def test_frr_bmpandsnmp(self): +        # test empty config section +        self.cli_set(base_path + ['bmp']) +        self.cli_set(base_path + ['snmp', 'bgpd']) +        self.cli_commit() + +        # read the config file and check content +        daemons_config = read_file(config_file) +        daemons_config_dict = daemons_config_parse(daemons_config) +        # prepare regex +        regex_snmp = re.compile(r'^.* -M bmp.*$') +        regex_snmp = re.compile(r'^.* -M snmp.*$') +        bmp_enabled = regex_snmp.match(daemons_config_dict['bgpd']) +        snmp_enabled = regex_snmp.match(daemons_config_dict['bgpd']) +        self.assertTrue(bmp_enabled) +        self.assertTrue(snmp_enabled) + + +if __name__ == '__main__': +    unittest.main(verbosity=2, failfast=True) diff --git a/smoketest/scripts/cli/test_vrf.py b/smoketest/scripts/cli/test_vrf.py index ff18f7261..176c095fb 100755 --- a/smoketest/scripts/cli/test_vrf.py +++ b/smoketest/scripts/cli/test_vrf.py @@ -127,6 +127,9 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):          for vrf in vrfs:              # Ensure VRF was created              self.assertIn(vrf, interfaces()) +            # Verify IP forwarding is 1 (enabled) +            self.assertEqual(read_file(f'/proc/sys/net/ipv4/conf/{vrf}/forwarding'), '1') +            self.assertEqual(read_file(f'/proc/sys/net/ipv6/conf/{vrf}/forwarding'), '1')              # Test for proper loopback IP assignment              for addr in loopbacks:                  self.assertTrue(is_intf_addr_assigned(vrf, addr)) @@ -267,5 +270,26 @@ class VRFTest(VyOSUnitTestSHIM.TestCase):          self.cli_delete(['interfaces', 'dummy', interface])          self.cli_commit() +    def test_vrf_disable_forwarding(self): +        table = '2000' +        for vrf in vrfs: +            base = base_path + ['name', vrf] +            self.cli_set(base + ['table', table]) +            self.cli_set(base + ['ip', 'disable-forwarding']) +            self.cli_set(base + ['ipv6', 'disable-forwarding']) +            table = str(int(table) + 1) + +        # commit changes +        self.cli_commit() + +        # Verify VRF configuration +        loopbacks = ['127.0.0.1', '::1'] +        for vrf in vrfs: +            # Ensure VRF was created +            self.assertIn(vrf, interfaces()) +            # Verify IP forwarding is 0 (disabled) +            self.assertEqual(read_file(f'/proc/sys/net/ipv4/conf/{vrf}/forwarding'), '0') +            self.assertEqual(read_file(f'/proc/sys/net/ipv6/conf/{vrf}/forwarding'), '0') +  if __name__ == '__main__':      unittest.main(verbosity=2) diff --git a/src/conf_mode/bcast_relay.py b/src/conf_mode/bcast_relay.py index d93a2a8f4..39a2971ce 100755 --- a/src/conf_mode/bcast_relay.py +++ b/src/conf_mode/bcast_relay.py @@ -1,6 +1,6 @@  #!/usr/bin/env python3  # -# Copyright (C) 2017-2020 VyOS maintainers and contributors +# Copyright (C) 2017-2022 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 @@ -78,7 +78,7 @@ def generate(relay):              continue          config['instance'] = instance -        render(config_file_base + instance, 'bcast-relay/udp-broadcast-relay.tmpl', +        render(config_file_base + instance, 'bcast-relay/udp-broadcast-relay.j2',                 config)      return None diff --git a/src/conf_mode/conntrack.py b/src/conf_mode/conntrack.py index aabf2bdf5..82289526f 100755 --- a/src/conf_mode/conntrack.py +++ b/src/conf_mode/conntrack.py @@ -101,9 +101,9 @@ def verify(conntrack):      return None  def generate(conntrack): -    render(conntrack_config, 'conntrack/vyos_nf_conntrack.conf.tmpl', conntrack) -    render(sysctl_file, 'conntrack/sysctl.conf.tmpl', conntrack) -    render(nftables_ct_file, 'conntrack/nftables-ct.tmpl', conntrack) +    render(conntrack_config, 'conntrack/vyos_nf_conntrack.conf.j2', conntrack) +    render(sysctl_file, 'conntrack/sysctl.conf.j2', conntrack) +    render(nftables_ct_file, 'conntrack/nftables-ct.j2', conntrack)      # dry-run newly generated configuration      tmp = run(f'nft -c -f {nftables_ct_file}') diff --git a/src/conf_mode/conntrack_sync.py b/src/conf_mode/conntrack_sync.py index 34d1f7398..c4b2bb488 100755 --- a/src/conf_mode/conntrack_sync.py +++ b/src/conf_mode/conntrack_sync.py @@ -111,11 +111,12 @@ def generate(conntrack):              os.unlink(config_file)          return None -    render(config_file, 'conntrackd/conntrackd.conf.tmpl', conntrack) +    render(config_file, 'conntrackd/conntrackd.conf.j2', conntrack)      return None  def apply(conntrack): +    systemd_service = 'conntrackd.service'      if not conntrack:          # Failover mechanism daemon should be indicated that it no longer needs          # to execute conntrackd actions on transition. This is only required @@ -123,7 +124,7 @@ def apply(conntrack):          if process_named_running('conntrackd'):              resync_vrrp() -        call('systemctl stop conntrackd.service') +        call(f'systemctl stop {systemd_service}')          return None      # Failover mechanism daemon should be indicated that it needs to execute @@ -132,7 +133,7 @@ def apply(conntrack):      if not process_named_running('conntrackd'):          resync_vrrp() -    call('systemctl restart conntrackd.service') +    call(f'systemctl reload-or-restart {systemd_service}')      return None  if __name__ == '__main__': diff --git a/src/conf_mode/containers.py b/src/conf_mode/container.py index 1cc6f5a35..2110fd9e0 100755 --- a/src/conf_mode/containers.py +++ b/src/conf_mode/container.py @@ -15,13 +15,13 @@  # along with this program.  If not, see <http://www.gnu.org/licenses/>.  import os -import json  from ipaddress import ip_address  from ipaddress import ip_network  from time import sleep  from json import dumps as json_write +from vyos.base import Warning  from vyos.config import Config  from vyos.configdict import dict_merge  from vyos.configdict import node_changed @@ -110,15 +110,21 @@ def verify(container):              if 'image' not in container_config:                  raise ConfigError(f'Container image for "{name}" is mandatory!') -            # verify container image exists locally -            image = container_config['image'] -              # Check if requested container image exists locally. If it does not -            # exist locally - inform the user. +            # exist locally - inform the user. This is required as there is a +            # shared container image storage accross all VyOS images. A user can +            # delete a container image from the system, boot into another version +            # of VyOS and then it would fail to boot. This is to prevent any +            # configuration error when container images are deleted from the +            # global storage. A per image local storage would be a super waste +            # of diskspace as there will be a full copy (up tu several GB/image) +            # on upgrade. This is the "cheapest" and fastest solution in terms +            # of image upgrade and deletion. +            image = container_config['image']              if run(f'podman image exists {image}') != 0: -                raise ConfigError(f'Image "{image}" used in contianer "{name}" does not exist '\ -                                  f'locally.\nPlease use "add container image {image}" to add it '\ -                                  'to the system!') +                Warning(f'Image "{image}" used in contianer "{name}" does not exist '\ +                        f'locally. Please use "add container image {image}" to add it '\ +                        f'to the system! Container "{name}" will not be started!')              if 'network' in container_config:                  if len(container_config['network']) > 1: @@ -254,8 +260,8 @@ def generate(container):              write_file(f'/etc/cni/net.d/{network}.conflist', json_write(tmp, indent=2)) -    render(config_containers_registry, 'containers/registries.conf.j2', container) -    render(config_containers_storage, 'containers/storage.conf.j2', container) +    render(config_containers_registry, 'container/registries.conf.j2', container) +    render(config_containers_storage, 'container/storage.conf.j2', container)      return None @@ -279,6 +285,11 @@ def apply(container):          for name, container_config in container['name'].items():              image = container_config['image'] +            if run(f'podman image exists {image}') != 0: +                # container image does not exist locally - user already got +                # informed by a WARNING in verfiy() - bail out early +                continue +              if 'disable' in container_config:                  # check if there is a container by that name running                  tmp = _cmd('podman ps -a --format "{{.Names}}"') diff --git a/src/conf_mode/firewall.py b/src/conf_mode/firewall.py index de78d53a8..6924bf555 100755 --- a/src/conf_mode/firewall.py +++ b/src/conf_mode/firewall.py @@ -327,8 +327,8 @@ def generate(firewall):      else:          firewall['cleanup_commands'] = cleanup_commands(firewall) -    render(nftables_conf, 'firewall/nftables.tmpl', firewall) -    render(nftables_defines_conf, 'firewall/nftables-defines.tmpl', firewall) +    render(nftables_conf, 'firewall/nftables.j2', firewall) +    render(nftables_defines_conf, 'firewall/nftables-defines.j2', firewall)      return None  def apply_sysfs(firewall): diff --git a/src/conf_mode/flow_accounting_conf.py b/src/conf_mode/flow_accounting_conf.py index 25bf54790..7750c1247 100755 --- a/src/conf_mode/flow_accounting_conf.py +++ b/src/conf_mode/flow_accounting_conf.py @@ -22,6 +22,7 @@ import ipaddress  from ipaddress import ip_address +from vyos.base import Warning  from vyos.config import Config  from vyos.configdict import dict_merge  from vyos.ifconfig import Section @@ -109,6 +110,9 @@ def _nftables_config(configured_ifaces, direction, length=None):          iface_prefix = "o" if direction == "egress" else "i"          rule_definition = f'{iface_prefix}ifname "{iface}" counter log group 2 snaplen {length} queue-threshold 100 comment "FLOW_ACCOUNTING_RULE"'          nftable_commands.append(f'nft insert rule {nftables_table} {nftables_chain} {rule_definition}') +        # Also add IPv6 ingres logging +        if nftables_table == nftables_nflog_table: +            nftable_commands.append(f'nft insert rule ip6 {nftables_table} {nftables_chain} {rule_definition}')      # change nftables      for command in nftable_commands: @@ -172,7 +176,7 @@ def verify(flow_config):          if interface not in Section.interfaces():              # Changed from error to warning to allow adding dynamic interfaces              # and interface templates -            print(f'Warning: Interface "{interface}" is not presented in the system') +            Warning(f'Interface "{interface}" is not presented in the system')      # check sFlow configuration      if 'sflow' in flow_config: @@ -200,7 +204,13 @@ def verify(flow_config):          if 'agent_address' in flow_config['sflow']:              tmp = flow_config['sflow']['agent_address']              if not is_addr_assigned(tmp): -                print(f'Warning: Configured "sflow agent-address {tmp}" does not exist in the system!') +                raise ConfigError(f'Configured "sflow agent-address {tmp}" does not exist in the system!') + +        # Check if configured netflow source-address exist in the system +        if 'source_address' in flow_config['sflow']: +            if not is_addr_assigned(flow_config['sflow']['source_address']): +                tmp = flow_config['sflow']['source_address'] +                raise ConfigError(f'Configured "sflow source-address {tmp}" does not exist on the system!')      # check NetFlow configuration      if 'netflow' in flow_config: @@ -212,7 +222,7 @@ def verify(flow_config):          if 'source_address' in flow_config['netflow']:              if not is_addr_assigned(flow_config['netflow']['source_address']):                  tmp = flow_config['netflow']['source_address'] -                print(f'Warning: Configured "netflow source-address {tmp}" does not exist on the system!') +                raise ConfigError(f'Configured "netflow source-address {tmp}" does not exist on the system!')          # Check if engine-id compatible with selected protocol version          if 'engine_id' in flow_config['netflow']: @@ -239,8 +249,8 @@ def generate(flow_config):      if not flow_config:          return None -    render(uacctd_conf_path, 'pmacct/uacctd.conf.tmpl', flow_config) -    render(systemd_override, 'pmacct/override.conf.tmpl', flow_config) +    render(uacctd_conf_path, 'pmacct/uacctd.conf.j2', flow_config) +    render(systemd_override, 'pmacct/override.conf.j2', flow_config)      # Reload systemd manager configuration      call('systemctl daemon-reload') diff --git a/src/conf_mode/high-availability.py b/src/conf_mode/high-availability.py index 7d51bb393..e14050dd3 100755 --- a/src/conf_mode/high-availability.py +++ b/src/conf_mode/high-availability.py @@ -28,7 +28,6 @@ from vyos.template import render  from vyos.template import is_ipv4  from vyos.template import is_ipv6  from vyos.util import call -from vyos.util import is_systemd_service_running  from vyos.xml import defaults  from vyos import ConfigError  from vyos import airbag @@ -152,7 +151,7 @@ def generate(ha):      if not ha:          return None -    render(VRRP.location['config'], 'high-availability/keepalived.conf.tmpl', ha) +    render(VRRP.location['config'], 'high-availability/keepalived.conf.j2', ha)      return None  def apply(ha): @@ -161,12 +160,7 @@ def apply(ha):          call(f'systemctl stop {service_name}')          return None -    # XXX: T3944 - reload keepalived configuration if service is already running -    # to not cause any service disruption when applying changes. -    if is_systemd_service_running(service_name): -        call(f'systemctl reload {service_name}') -    else: -        call(f'systemctl restart {service_name}') +    call(f'systemctl reload-or-restart {service_name}')      return None  if __name__ == '__main__': diff --git a/src/conf_mode/http-api.py b/src/conf_mode/http-api.py index 00f3d4f7f..4a7906c17 100755 --- a/src/conf_mode/http-api.py +++ b/src/conf_mode/http-api.py @@ -117,7 +117,7 @@ def generate(http_api):      with open(api_conf_file, 'w') as f:          json.dump(http_api, f, indent=2) -    render(systemd_service, 'https/vyos-http-api.service.tmpl', http_api) +    render(systemd_service, 'https/vyos-http-api.service.j2', http_api)      return None  def apply(http_api): diff --git a/src/conf_mode/https.py b/src/conf_mode/https.py index 37fa36797..3057357fc 100755 --- a/src/conf_mode/https.py +++ b/src/conf_mode/https.py @@ -214,8 +214,8 @@ def generate(https):          'certbot': certbot      } -    render(config_file, 'https/nginx.default.tmpl', data) -    render(systemd_override, 'https/override.conf.tmpl', https) +    render(config_file, 'https/nginx.default.j2', data) +    render(systemd_override, 'https/override.conf.j2', https)      return None  def apply(https): diff --git a/src/conf_mode/igmp_proxy.py b/src/conf_mode/igmp_proxy.py index 37df3dc92..de6a51c64 100755 --- a/src/conf_mode/igmp_proxy.py +++ b/src/conf_mode/igmp_proxy.py @@ -96,7 +96,7 @@ def generate(igmp_proxy):          Warning('IGMP Proxy will be deactivated because it is disabled')          return None -    render(config_file, 'igmp-proxy/igmpproxy.conf.tmpl', igmp_proxy) +    render(config_file, 'igmp-proxy/igmpproxy.conf.j2', igmp_proxy)      return None diff --git a/src/conf_mode/interfaces-pppoe.py b/src/conf_mode/interfaces-pppoe.py index 26daa8381..e2fdc7a42 100755 --- a/src/conf_mode/interfaces-pppoe.py +++ b/src/conf_mode/interfaces-pppoe.py @@ -92,7 +92,7 @@ def generate(pppoe):          return None      # Create PPP configuration files -    render(config_pppoe, 'pppoe/peer.tmpl', pppoe, permission=0o640) +    render(config_pppoe, 'pppoe/peer.j2', pppoe, permission=0o640)      return None diff --git a/src/conf_mode/lldp.py b/src/conf_mode/lldp.py index 2bb615eb7..c703c1fe0 100755 --- a/src/conf_mode/lldp.py +++ b/src/conf_mode/lldp.py @@ -111,8 +111,8 @@ def generate(lldp):      if lldp is None:          return -    render(config_file, 'lldp/lldpd.tmpl', lldp) -    render(vyos_config_file, 'lldp/vyos.conf.tmpl', lldp) +    render(config_file, 'lldp/lldpd.j2', lldp) +    render(vyos_config_file, 'lldp/vyos.conf.j2', lldp)  def apply(lldp):      systemd_service = 'lldpd.service' diff --git a/src/conf_mode/nat.py b/src/conf_mode/nat.py index 8aaebf9ff..85819a77e 100755 --- a/src/conf_mode/nat.py +++ b/src/conf_mode/nat.py @@ -181,7 +181,7 @@ def verify(nat):      return None  def generate(nat): -    render(nftables_nat_config, 'firewall/nftables-nat.tmpl', nat) +    render(nftables_nat_config, 'firewall/nftables-nat.j2', nat)      # dry-run newly generated configuration      tmp = run(f'nft -c -f {nftables_nat_config}') diff --git a/src/conf_mode/nat66.py b/src/conf_mode/nat66.py index 1cd15811f..0972151a0 100755 --- a/src/conf_mode/nat66.py +++ b/src/conf_mode/nat66.py @@ -146,8 +146,8 @@ def verify(nat):      return None  def generate(nat): -    render(nftables_nat66_config, 'firewall/nftables-nat66.tmpl', nat, permission=0o755) -    render(ndppd_config, 'ndppd/ndppd.conf.tmpl', nat, permission=0o755) +    render(nftables_nat66_config, 'firewall/nftables-nat66.j2', nat, permission=0o755) +    render(ndppd_config, 'ndppd/ndppd.conf.j2', nat, permission=0o755)      return None  def apply(nat): diff --git a/src/conf_mode/policy-route.py b/src/conf_mode/policy-route.py index 09d181d43..5de341beb 100755 --- a/src/conf_mode/policy-route.py +++ b/src/conf_mode/policy-route.py @@ -204,7 +204,7 @@ def generate(policy):      else:          policy['cleanup_commands'] = cleanup_commands(policy) -    render(nftables_conf, 'firewall/nftables-policy.tmpl', policy) +    render(nftables_conf, 'firewall/nftables-policy.j2', policy)      return None  def apply_table_marks(policy): diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py index a9173ab87..cd46cbcb4 100755 --- a/src/conf_mode/protocols_bgp.py +++ b/src/conf_mode/protocols_bgp.py @@ -169,6 +169,16 @@ def verify(bgp):                      peer_group = peer_config['peer_group']                      if 'remote_as' in peer_config and 'remote_as' in bgp['peer_group'][peer_group]:                          raise ConfigError(f'Peer-group member "{peer}" cannot override remote-as of peer-group "{peer_group}"!') +                if 'interface' in peer_config: +                    if 'peer_group' in peer_config['interface']: +                        peer_group = peer_config['interface']['peer_group'] +                        if 'remote_as' in peer_config['interface'] and 'remote_as' in bgp['peer_group'][peer_group]: +                            raise ConfigError(f'Peer-group member "{peer}" cannot override remote-as of peer-group "{peer_group}"!') +                    if 'v6only' in peer_config['interface']: +                        if 'peer_group' in peer_config['interface']['v6only']: +                            peer_group = peer_config['interface']['v6only']['peer_group'] +                            if 'remote_as' in peer_config['interface']['v6only'] and 'remote_as' in bgp['peer_group'][peer_group]: +                                raise ConfigError(f'Peer-group member "{peer}" cannot override remote-as of peer-group "{peer_group}"!')                  # Only checks for ipv4 and ipv6 neighbors                  # Check if neighbor address is assigned as system interface address diff --git a/src/conf_mode/protocols_nhrp.py b/src/conf_mode/protocols_nhrp.py index ff8ae8eeb..56939955d 100755 --- a/src/conf_mode/protocols_nhrp.py +++ b/src/conf_mode/protocols_nhrp.py @@ -1,6 +1,6 @@  #!/usr/bin/env python3  # -# Copyright (C) 2021 VyOS maintainers and contributors +# Copyright (C) 2021-2022 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 @@ -81,10 +81,15 @@ def verify(nhrp):                  for map_name, map_conf in nhrp_conf['dynamic_map'].items():                      if 'nbma_domain_name' not in map_conf:                          raise ConfigError(f'nbma-domain-name missing on dynamic-map {map_name} on tunnel {name}') + +            if 'cisco_authentication' in nhrp_conf: +                if len(nhrp_conf['cisco_authentication']) > 8: +                    raise ConfigError('Maximum length of the secret is 8 characters!') +      return None  def generate(nhrp): -    render(opennhrp_conf, 'nhrp/opennhrp.conf.tmpl', nhrp) +    render(opennhrp_conf, 'nhrp/opennhrp.conf.j2', nhrp)      return None  def apply(nhrp): @@ -104,8 +109,8 @@ def apply(nhrp):          if rule_handle:              remove_nftables_rule('ip filter', 'VYOS_FW_OUTPUT', rule_handle) -    action = 'reload-or-restart' if nhrp and 'tunnel' in nhrp else 'stop' -    run(f'systemctl {action} opennhrp') +    action = 'restart' if nhrp and 'tunnel' in nhrp else 'stop' +    run(f'systemctl {action} opennhrp.service')      return None  if __name__ == '__main__': diff --git a/src/conf_mode/service_console-server.py b/src/conf_mode/service_console-server.py index 51050e702..a2e411e49 100755 --- a/src/conf_mode/service_console-server.py +++ b/src/conf_mode/service_console-server.py @@ -81,7 +81,7 @@ def generate(proxy):      if not proxy:          return None -    render(config_file, 'conserver/conserver.conf.tmpl', proxy) +    render(config_file, 'conserver/conserver.conf.j2', proxy)      if 'device' in proxy:          for device, device_config in proxy['device'].items():              if 'ssh' not in device_config: @@ -92,7 +92,7 @@ def generate(proxy):                  'port' : device_config['ssh']['port'],              }              render(dropbear_systemd_file.format(**tmp), -                   'conserver/dropbear@.service.tmpl', tmp) +                   'conserver/dropbear@.service.j2', tmp)      return None diff --git a/src/conf_mode/service_ids_fastnetmon.py b/src/conf_mode/service_ids_fastnetmon.py index 67edeb630..ae7e582ec 100755 --- a/src/conf_mode/service_ids_fastnetmon.py +++ b/src/conf_mode/service_ids_fastnetmon.py @@ -67,8 +67,8 @@ def generate(fastnetmon):          return -    render(config_file, 'ids/fastnetmon.tmpl', fastnetmon) -    render(networks_list, 'ids/fastnetmon_networks_list.tmpl', fastnetmon) +    render(config_file, 'ids/fastnetmon.j2', fastnetmon) +    render(networks_list, 'ids/fastnetmon_networks_list.j2', fastnetmon)      return None diff --git a/src/conf_mode/service_ipoe-server.py b/src/conf_mode/service_ipoe-server.py index 2ebee8018..559d1bcd5 100755 --- a/src/conf_mode/service_ipoe-server.py +++ b/src/conf_mode/service_ipoe-server.py @@ -296,10 +296,10 @@ def generate(ipoe):      if not ipoe:          return None -    render(ipoe_conf, 'accel-ppp/ipoe.config.tmpl', ipoe) +    render(ipoe_conf, 'accel-ppp/ipoe.config.j2', ipoe)      if ipoe['auth_mode'] == 'local': -        render(ipoe_chap_secrets, 'accel-ppp/chap-secrets.ipoe.tmpl', ipoe) +        render(ipoe_chap_secrets, 'accel-ppp/chap-secrets.ipoe.j2', ipoe)          os.chmod(ipoe_chap_secrets, S_IRUSR | S_IWUSR | S_IRGRP)      else: diff --git a/src/conf_mode/service_mdns-repeater.py b/src/conf_mode/service_mdns-repeater.py index d31a0c49e..2383a53fb 100755 --- a/src/conf_mode/service_mdns-repeater.py +++ b/src/conf_mode/service_mdns-repeater.py @@ -1,6 +1,6 @@  #!/usr/bin/env python3  # -# Copyright (C) 2017-2020 VyOS maintainers and contributors +# Copyright (C) 2017-2022 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 @@ -92,7 +92,7 @@ def generate(mdns):          if len(mdns['interface']) < 2:              return None -    render(config_file, 'mdns-repeater/avahi-daemon.tmpl', mdns) +    render(config_file, 'mdns-repeater/avahi-daemon.j2', mdns)      return None  def apply(mdns): diff --git a/src/conf_mode/service_monitoring_telegraf.py b/src/conf_mode/service_monitoring_telegraf.py index 8a972b9fe..daf75d740 100755 --- a/src/conf_mode/service_monitoring_telegraf.py +++ b/src/conf_mode/service_monitoring_telegraf.py @@ -99,6 +99,32 @@ def get_config(config=None):      monitoring['interfaces_ethernet'] = get_interfaces('ethernet', vlan=False)      monitoring['nft_chains'] = get_nft_filter_chains() +    if 'authentication' in monitoring or \ +       'url' in monitoring: +        monitoring['influxdb_configured'] = True + +    # Redefine azure group-metrics 'single-table' and 'table-per-metric' +    if 'azure_data_explorer' in monitoring: +        if 'single-table' in monitoring['azure_data_explorer']['group_metrics']: +            monitoring['azure_data_explorer']['group_metrics'] = 'SingleTable' +        else: +            monitoring['azure_data_explorer']['group_metrics'] = 'TablePerMetric' +        # Set azure env +        if 'authentication' in monitoring['azure_data_explorer']: +            auth_config = monitoring['azure_data_explorer']['authentication'] +            if {'client_id', 'client_secret', 'tenant_id'} <= set(auth_config): +                os.environ['AZURE_CLIENT_ID'] = auth_config['client_id'] +                os.environ['AZURE_CLIENT_SECRET'] = auth_config['client_secret'] +                os.environ['AZURE_TENANT_ID'] = auth_config['tenant_id'] + +    # Ignore default XML values if config doesn't exists +    # Delete key from dict +    if not conf.exists(base + ['prometheus-client']): +        del monitoring['prometheus_client'] + +    if not conf.exists(base + ['azure-data-explorer']): +        del monitoring['azure_data_explorer'] +      return monitoring  def verify(monitoring): @@ -106,13 +132,41 @@ def verify(monitoring):      if not monitoring:          return None -    if 'authentication' not in monitoring or \ -       'organization' not in monitoring['authentication'] or \ -       'token' not in monitoring['authentication']: -        raise ConfigError(f'Authentication "organization and token" are mandatory!') +    if 'influxdb_configured' in monitoring: +        if 'authentication' not in monitoring or \ +           'organization' not in monitoring['authentication'] or \ +           'token' not in monitoring['authentication']: +            raise ConfigError(f'Authentication "organization and token" are mandatory!') + +        if 'url' not in monitoring: +            raise ConfigError(f'Monitoring "url" is mandatory!') + +    # Verify azure-data-explorer +    if 'azure_data_explorer' in monitoring: +        if 'authentication' not in monitoring['azure_data_explorer'] or \ +           'client_id' not in monitoring['azure_data_explorer']['authentication'] or \ +           'client_secret' not in monitoring['azure_data_explorer']['authentication'] or \ +           'tenant_id' not in monitoring['azure_data_explorer']['authentication']: +            raise ConfigError(f'Authentication "client-id, client-secret and tenant-id" are mandatory!') + +        if 'database' not in monitoring['azure_data_explorer']: +            raise ConfigError(f'Monitoring "database" is mandatory!') + +        if 'url' not in monitoring['azure_data_explorer']: +            raise ConfigError(f'Monitoring "url" is mandatory!') + +        if monitoring['azure_data_explorer']['group_metrics'] == 'SingleTable' and \ +            'table' not in monitoring['azure_data_explorer']: +             raise ConfigError(f'Monitoring "table" name for single-table mode is mandatory!') + +    # Verify Splunk +    if 'splunk' in monitoring: +        if 'authentication' not in monitoring['splunk'] or \ +           'token' not in monitoring['splunk']['authentication']: +            raise ConfigError(f'Authentication "organization and token" are mandatory!') -    if 'url' not in monitoring: -        raise ConfigError(f'Monitoring "url" is mandatory!') +        if 'url' not in monitoring['splunk']: +            raise ConfigError(f'Monitoring splunk "url" is mandatory!')      return None @@ -145,10 +199,10 @@ def generate(monitoring):          os.mkdir(custom_scripts_dir)      # Render telegraf configuration and systemd override -    render(config_telegraf, 'monitoring/telegraf.tmpl', monitoring) -    render(systemd_telegraf_service, 'monitoring/systemd_vyos_telegraf_service.tmpl', monitoring) -    render(systemd_override, 'monitoring/override.conf.tmpl', monitoring, permission=0o640) -    render(syslog_telegraf, 'monitoring/syslog_telegraf.tmpl', monitoring) +    render(config_telegraf, 'monitoring/telegraf.j2', monitoring) +    render(systemd_telegraf_service, 'monitoring/systemd_vyos_telegraf_service.j2', monitoring) +    render(systemd_override, 'monitoring/override.conf.j2', monitoring, permission=0o640) +    render(syslog_telegraf, 'monitoring/syslog_telegraf.j2', monitoring)      chown(base_dir, 'telegraf', 'telegraf') diff --git a/src/conf_mode/service_pppoe-server.py b/src/conf_mode/service_pppoe-server.py index 1f31d132d..6086ef859 100755 --- a/src/conf_mode/service_pppoe-server.py +++ b/src/conf_mode/service_pppoe-server.py @@ -88,10 +88,10 @@ def generate(pppoe):              for vlan_range in pppoe['interface'][iface]['vlan_range']:                  pppoe['interface'][iface]['regex'].append(range_to_regex(vlan_range)) -    render(pppoe_conf, 'accel-ppp/pppoe.config.tmpl', pppoe) +    render(pppoe_conf, 'accel-ppp/pppoe.config.j2', pppoe)      if dict_search('authentication.mode', pppoe) == 'local': -        render(pppoe_chap_secrets, 'accel-ppp/chap-secrets.config_dict.tmpl', +        render(pppoe_chap_secrets, 'accel-ppp/chap-secrets.config_dict.j2',                 pppoe, permission=0o640)      else:          if os.path.exists(pppoe_chap_secrets): diff --git a/src/conf_mode/service_router-advert.py b/src/conf_mode/service_router-advert.py index 9afcdd63e..71b758399 100755 --- a/src/conf_mode/service_router-advert.py +++ b/src/conf_mode/service_router-advert.py @@ -101,7 +101,7 @@ def generate(rtradv):      if not rtradv:          return None -    render(config_file, 'router-advert/radvd.conf.tmpl', rtradv, permission=0o644) +    render(config_file, 'router-advert/radvd.conf.j2', rtradv, permission=0o644)      return None  def apply(rtradv): diff --git a/src/conf_mode/service_upnp.py b/src/conf_mode/service_upnp.py index d21b31990..36f3e18a7 100755 --- a/src/conf_mode/service_upnp.py +++ b/src/conf_mode/service_upnp.py @@ -135,7 +135,7 @@ def generate(upnpd):      if os.path.isfile(config_file):          os.unlink(config_file) -    render(config_file, 'firewall/upnpd.conf.tmpl', upnpd) +    render(config_file, 'firewall/upnpd.conf.j2', upnpd)  def apply(upnpd):      systemd_service_name = 'miniupnpd.service' diff --git a/src/conf_mode/service_webproxy.py b/src/conf_mode/service_webproxy.py index a16cc4aeb..32af31bde 100755 --- a/src/conf_mode/service_webproxy.py +++ b/src/conf_mode/service_webproxy.py @@ -61,7 +61,7 @@ def generate_sg_localdb(category, list_type, role, proxy):                     user=user_group, group=user_group)          # temporary config file, deleted after generation -        render(sg_tmp_file, 'squid/sg_acl.conf.tmpl', tmp, +        render(sg_tmp_file, 'squid/sg_acl.conf.j2', tmp,                 user=user_group, group=user_group)          call(f'su - {user_group} -c "squidGuard -d -c {sg_tmp_file} -C {db_file}"') @@ -166,8 +166,8 @@ def generate(proxy):      if not proxy:          return None -    render(squid_config_file, 'squid/squid.conf.tmpl', proxy) -    render(squidguard_config_file, 'squid/squidGuard.conf.tmpl', proxy) +    render(squid_config_file, 'squid/squid.conf.j2', proxy) +    render(squidguard_config_file, 'squid/squidGuard.conf.j2', proxy)      cat_dict = {          'local-block' : 'domains', diff --git a/src/conf_mode/snmp.py b/src/conf_mode/snmp.py index e35bb8a0c..5cd24db32 100755 --- a/src/conf_mode/snmp.py +++ b/src/conf_mode/snmp.py @@ -270,15 +270,15 @@ def generate(snmp):                      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) +    render(config_file_client, 'snmp/etc.snmp.conf.j2', snmp)      # Write server config file -    render(config_file_daemon, 'snmp/etc.snmpd.conf.tmpl', snmp) +    render(config_file_daemon, 'snmp/etc.snmpd.conf.j2', snmp)      # Write access rights config file -    render(config_file_access, 'snmp/usr.snmpd.conf.tmpl', snmp) +    render(config_file_access, 'snmp/usr.snmpd.conf.j2', snmp)      # Write access rights config file -    render(config_file_user, 'snmp/var.snmpd.conf.tmpl', snmp) +    render(config_file_user, 'snmp/var.snmpd.conf.j2', snmp)      # Write daemon configuration file -    render(systemd_override, 'snmp/override.conf.tmpl', snmp) +    render(systemd_override, 'snmp/override.conf.j2', snmp)      return None @@ -293,7 +293,15 @@ def apply(snmp):      call(f'systemctl restart {systemd_service}')      # Enable AgentX in FRR -    call('vtysh -c "configure terminal" -c "agentx" >/dev/null') +    # This should be done for each daemon individually because common command +    # works only if all the daemons started with SNMP support +    frr_daemons_list = [ +        'bgpd', 'ospf6d', 'ospfd', 'ripd', 'ripngd', 'isisd', 'ldpd', 'zebra' +    ] +    for frr_daemon in frr_daemons_list: +        call( +            f'vtysh -c "configure terminal" -d {frr_daemon} -c "agentx" >/dev/null' +        )      return None diff --git a/src/conf_mode/ssh.py b/src/conf_mode/ssh.py index 487e8c229..28669694b 100755 --- a/src/conf_mode/ssh.py +++ b/src/conf_mode/ssh.py @@ -1,6 +1,6 @@  #!/usr/bin/env python3  # -# Copyright (C) 2018-2021 VyOS maintainers and contributors +# Copyright (C) 2018-2022 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 @@ -33,6 +33,9 @@ airbag.enable()  config_file = r'/run/sshd/sshd_config'  systemd_override = r'/etc/systemd/system/ssh.service.d/override.conf' +sshguard_config_file = '/etc/sshguard/sshguard.conf' +sshguard_whitelist = '/etc/sshguard/whitelist' +  key_rsa = '/etc/ssh/ssh_host_rsa_key'  key_dsa = '/etc/ssh/ssh_host_dsa_key'  key_ed25519 = '/etc/ssh/ssh_host_ed25519_key' @@ -54,6 +57,11 @@ def get_config(config=None):      # pass config file path - used in override template      ssh['config_file'] = config_file +    # Ignore default XML values if config doesn't exists +    # Delete key from dict +    if not conf.exists(base + ['dynamic-protection']): +         del ssh['dynamic_protection'] +      return ssh  def verify(ssh): @@ -86,6 +94,10 @@ def generate(ssh):      render(config_file, 'ssh/sshd_config.j2', ssh)      render(systemd_override, 'ssh/override.conf.j2', ssh) + +    if 'dynamic_protection' in ssh: +        render(sshguard_config_file, 'ssh/sshguard_config.j2', ssh) +        render(sshguard_whitelist, 'ssh/sshguard_whitelist.j2', ssh)      # Reload systemd manager configuration      call('systemctl daemon-reload') @@ -95,7 +107,12 @@ def apply(ssh):      if not ssh:          # SSH access is removed in the commit          call('systemctl stop ssh.service') +        call('systemctl stop sshguard.service')          return None +    if 'dynamic_protection' not in ssh: +        call('systemctl stop sshguard.service') +    else: +        call('systemctl restart sshguard.service')      call('systemctl restart ssh.service')      return None diff --git a/src/conf_mode/system-login.py b/src/conf_mode/system-login.py index c9c6aa187..c717286ae 100755 --- a/src/conf_mode/system-login.py +++ b/src/conf_mode/system-login.py @@ -197,7 +197,7 @@ def generate(login):                      pass      if 'radius' in login: -        render(radius_config_file, 'login/pam_radius_auth.conf.tmpl', login, +        render(radius_config_file, 'login/pam_radius_auth.conf.j2', login,                     permission=0o600, user='root', group='root')      else:          if os.path.isfile(radius_config_file): @@ -241,7 +241,7 @@ def apply(login):                  #                  # XXX: Should we deny using root at all?                  home_dir = getpwnam(user).pw_dir -                render(f'{home_dir}/.ssh/authorized_keys', 'login/authorized_keys.tmpl', +                render(f'{home_dir}/.ssh/authorized_keys', 'login/authorized_keys.j2',                         user_config, permission=0o600,                         formater=lambda _: _.replace(""", '"'),                         user=user, group='users') diff --git a/src/conf_mode/system-logs.py b/src/conf_mode/system-logs.py index e6296656d..c71938a79 100755 --- a/src/conf_mode/system-logs.py +++ b/src/conf_mode/system-logs.py @@ -57,13 +57,13 @@ def generate(logs_config):      logrotate_atop = dict_search('logrotate.atop', logs_config)      # generate new config file for atop      syslog.debug('Adding logrotate config for atop') -    render(logrotate_atop_file, 'logs/logrotate/vyos-atop.tmpl', logrotate_atop) +    render(logrotate_atop_file, 'logs/logrotate/vyos-atop.j2', logrotate_atop)      # get configuration for logrotate rsyslog      logrotate_rsyslog = dict_search('logrotate.messages', logs_config)      # generate new config file for rsyslog      syslog.debug('Adding logrotate config for rsyslog') -    render(logrotate_rsyslog_file, 'logs/logrotate/vyos-rsyslog.tmpl', +    render(logrotate_rsyslog_file, 'logs/logrotate/vyos-rsyslog.j2',             logrotate_rsyslog) diff --git a/src/conf_mode/system-option.py b/src/conf_mode/system-option.py index b1c63e316..36dbf155b 100755 --- a/src/conf_mode/system-option.py +++ b/src/conf_mode/system-option.py @@ -74,8 +74,8 @@ def verify(options):      return None  def generate(options): -    render(curlrc_config, 'system/curlrc.tmpl', options) -    render(ssh_config, 'system/ssh_config.tmpl', options) +    render(curlrc_config, 'system/curlrc.j2', options) +    render(ssh_config, 'system/ssh_config.j2', options)      return None  def apply(options): diff --git a/src/conf_mode/system-syslog.py b/src/conf_mode/system-syslog.py index 309b4bdb0..a9d3bbe31 100755 --- a/src/conf_mode/system-syslog.py +++ b/src/conf_mode/system-syslog.py @@ -204,7 +204,7 @@ def generate(c):          return None      conf = '/etc/rsyslog.d/vyos-rsyslog.conf' -    render(conf, 'syslog/rsyslog.conf.tmpl', c) +    render(conf, 'syslog/rsyslog.conf.j2', c)      # cleanup current logrotate config files      logrotate_files = Path('/etc/logrotate.d/').glob('vyos-rsyslog-generated-*') @@ -216,7 +216,7 @@ def generate(c):      for filename, fileconfig in c.get('files', {}).items():          if fileconfig['log-file'].startswith('/var/log/user/'):              conf = '/etc/logrotate.d/vyos-rsyslog-generated-' + filename -            render(conf, 'syslog/logrotate.tmpl', { 'config_render': fileconfig }) +            render(conf, 'syslog/logrotate.j2', { 'config_render': fileconfig })  def verify(c): diff --git a/src/conf_mode/system_console.py b/src/conf_mode/system_console.py index 19b252513..86985d765 100755 --- a/src/conf_mode/system_console.py +++ b/src/conf_mode/system_console.py @@ -103,7 +103,7 @@ def generate(console):          config_file = base_dir + f'/serial-getty@{device}.service'          getty_wants_symlink = base_dir + f'/getty.target.wants/serial-getty@{device}.service' -        render(config_file, 'getty/serial-getty.service.tmpl', device_config) +        render(config_file, 'getty/serial-getty.service.j2', device_config)          os.symlink(config_file, getty_wants_symlink)      # GRUB diff --git a/src/conf_mode/system_frr.py b/src/conf_mode/system_frr.py new file mode 100755 index 000000000..1af0055f6 --- /dev/null +++ b/src/conf_mode/system_frr.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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 pathlib import Path +from sys import exit + +from vyos import ConfigError +from vyos import airbag +from vyos.config import Config +from vyos.logger import syslog +from vyos.template import render_to_string +from vyos.util import read_file, write_file, run +airbag.enable() + +# path to daemons config and config status files +config_file = '/etc/frr/daemons' +vyos_status_file = '/tmp/vyos-config-status' +# path to watchfrr for FRR control +watchfrr = '/usr/lib/frr/watchfrr.sh' + + +def get_config(config=None): +    if config: +        conf = config +    else: +        conf = Config() + +    base = ['system', 'frr'] +    frr_config = conf.get_config_dict(base, get_first_key=True) + +    return frr_config + + +def verify(frr_config): +    # Nothing to verify here +    pass + + +def generate(frr_config): +    # read daemons config file +    daemons_config_current = read_file(config_file) +    # generate new config file +    daemons_config_new = render_to_string('frr/daemons.frr.tmpl', frr_config) +    # update configuration file if this is necessary +    if daemons_config_new != daemons_config_current: +        syslog.warning('FRR daemons configuration file need to be changed') +        write_file(config_file, daemons_config_new) +        frr_config['config_file_changed'] = True + + +def apply(frr_config): +    # check if this is initial commit during boot or intiated by CLI +    # if the file exists, this must be CLI commit +    commit_type_cli = Path(vyos_status_file).exists() +    # display warning to user +    if commit_type_cli and frr_config.get('config_file_changed'): +        # Since FRR restart is not safe thing, better to give +        # control over this to users +        print(''' +        You need to reboot a router (preferred) or restart FRR +        to apply changes in modules settings +        ''') +    # restart FRR automatically. DUring the initial boot this should be +    # safe in most cases +    if not commit_type_cli and frr_config.get('config_file_changed'): +        syslog.warning('Restarting FRR to apply changes in modules') +        run(f'{watchfrr} restart') + + +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/conf_mode/system_lcd.py b/src/conf_mode/system_lcd.py index b5ce32beb..3341dd738 100755 --- a/src/conf_mode/system_lcd.py +++ b/src/conf_mode/system_lcd.py @@ -1,6 +1,6 @@  #!/usr/bin/env python3  # -# Copyright 2020 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2020-2022 VyOS maintainers and contributors <maintainers@vyos.io>  #  # 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 @@ -61,9 +61,9 @@ def generate(lcd):          lcd['device'] = find_device_file(lcd['device'])      # Render config file for daemon LCDd -    render(lcdd_conf, 'lcd/LCDd.conf.tmpl', lcd) +    render(lcdd_conf, 'lcd/LCDd.conf.j2', lcd)      # Render config file for client lcdproc -    render(lcdproc_conf, 'lcd/lcdproc.conf.tmpl', lcd) +    render(lcdproc_conf, 'lcd/lcdproc.conf.j2', lcd)      return None diff --git a/src/conf_mode/system_sysctl.py b/src/conf_mode/system_sysctl.py index 4f16d1ed6..2e0004ffa 100755 --- a/src/conf_mode/system_sysctl.py +++ b/src/conf_mode/system_sysctl.py @@ -50,7 +50,7 @@ def generate(sysctl):              os.unlink(config_file)          return None -    render(config_file, 'system/sysctl.conf.tmpl', sysctl) +    render(config_file, 'system/sysctl.conf.j2', sysctl)      return None  def apply(sysctl): diff --git a/src/conf_mode/tftp_server.py b/src/conf_mode/tftp_server.py index 95050624e..c5daccb7f 100755 --- a/src/conf_mode/tftp_server.py +++ b/src/conf_mode/tftp_server.py @@ -98,7 +98,7 @@ def generate(tftpd):              config['vrf'] = address_config['vrf']          file = config_file + str(idx) -        render(file, 'tftp-server/default.tmpl', config) +        render(file, 'tftp-server/default.j2', config)          idx = idx + 1      return None diff --git a/src/conf_mode/vpn_ipsec.py b/src/conf_mode/vpn_ipsec.py index dc134fd1f..bad9cfbd8 100755 --- a/src/conf_mode/vpn_ipsec.py +++ b/src/conf_mode/vpn_ipsec.py @@ -503,7 +503,7 @@ def generate(ipsec):                              charon_radius_conf, interface_conf, swanctl_conf]:              if os.path.isfile(config_file):                  os.unlink(config_file) -        render(charon_conf, 'ipsec/charon.tmpl', {'install_routes': default_install_routes}) +        render(charon_conf, 'ipsec/charon.j2', {'install_routes': default_install_routes})          return      if ipsec['dhcp_no_address']: @@ -567,13 +567,13 @@ def generate(ipsec):                      ipsec['site_to_site']['peer'][peer]['tunnel'][tunnel]['passthrough'] = passthrough -    render(ipsec_conf, 'ipsec/ipsec.conf.tmpl', ipsec) -    render(ipsec_secrets, 'ipsec/ipsec.secrets.tmpl', ipsec) -    render(charon_conf, 'ipsec/charon.tmpl', ipsec) -    render(charon_dhcp_conf, 'ipsec/charon/dhcp.conf.tmpl', ipsec) -    render(charon_radius_conf, 'ipsec/charon/eap-radius.conf.tmpl', ipsec) -    render(interface_conf, 'ipsec/interfaces_use.conf.tmpl', ipsec) -    render(swanctl_conf, 'ipsec/swanctl.conf.tmpl', ipsec) +    render(ipsec_conf, 'ipsec/ipsec.conf.j2', ipsec) +    render(ipsec_secrets, 'ipsec/ipsec.secrets.j2', ipsec) +    render(charon_conf, 'ipsec/charon.j2', ipsec) +    render(charon_dhcp_conf, 'ipsec/charon/dhcp.conf.j2', ipsec) +    render(charon_radius_conf, 'ipsec/charon/eap-radius.conf.j2', ipsec) +    render(interface_conf, 'ipsec/interfaces_use.conf.j2', ipsec) +    render(swanctl_conf, 'ipsec/swanctl.conf.j2', ipsec)  def resync_nhrp(ipsec):      if ipsec and not ipsec['nhrp_exists']: diff --git a/src/conf_mode/vpn_l2tp.py b/src/conf_mode/vpn_l2tp.py index 818e8fa0b..fd5a4acd8 100755 --- a/src/conf_mode/vpn_l2tp.py +++ b/src/conf_mode/vpn_l2tp.py @@ -358,10 +358,10 @@ def generate(l2tp):      if not l2tp:          return None -    render(l2tp_conf, 'accel-ppp/l2tp.config.tmpl', l2tp) +    render(l2tp_conf, 'accel-ppp/l2tp.config.j2', l2tp)      if l2tp['auth_mode'] == 'local': -        render(l2tp_chap_secrets, 'accel-ppp/chap-secrets.tmpl', l2tp) +        render(l2tp_chap_secrets, 'accel-ppp/chap-secrets.j2', l2tp)          os.chmod(l2tp_chap_secrets, S_IRUSR | S_IWUSR | S_IRGRP)      else: diff --git a/src/conf_mode/vpn_openconnect.py b/src/conf_mode/vpn_openconnect.py index 84d31f9a5..8e0e30bbf 100755 --- a/src/conf_mode/vpn_openconnect.py +++ b/src/conf_mode/vpn_openconnect.py @@ -157,9 +157,9 @@ def generate(ocserv):      if "radius" in ocserv["authentication"]["mode"]:          # Render radius client configuration -        render(radius_cfg, 'ocserv/radius_conf.tmpl', ocserv["authentication"]["radius"]) +        render(radius_cfg, 'ocserv/radius_conf.j2', ocserv["authentication"]["radius"])          # Render radius servers -        render(radius_servers, 'ocserv/radius_servers.tmpl', ocserv["authentication"]["radius"]) +        render(radius_servers, 'ocserv/radius_servers.j2', ocserv["authentication"]["radius"])      elif "local" in ocserv["authentication"]["mode"]:          # if mode "OTP", generate OTP users file parameters          if "otp" in ocserv["authentication"]["mode"]["local"]: @@ -184,24 +184,24 @@ def generate(ocserv):          if "password-otp" in ocserv["authentication"]["mode"]["local"]:              # Render local users ocpasswd -            render(ocserv_passwd, 'ocserv/ocserv_passwd.tmpl', ocserv["authentication"]["local_users"]) +            render(ocserv_passwd, 'ocserv/ocserv_passwd.j2', ocserv["authentication"]["local_users"])              # Render local users OTP keys -            render(ocserv_otp_usr, 'ocserv/ocserv_otp_usr.tmpl', ocserv["authentication"]["local_users"]) +            render(ocserv_otp_usr, 'ocserv/ocserv_otp_usr.j2', ocserv["authentication"]["local_users"])          elif "password" in ocserv["authentication"]["mode"]["local"]:              # Render local users ocpasswd -            render(ocserv_passwd, 'ocserv/ocserv_passwd.tmpl', ocserv["authentication"]["local_users"]) +            render(ocserv_passwd, 'ocserv/ocserv_passwd.j2', ocserv["authentication"]["local_users"])          elif "otp" in ocserv["authentication"]["mode"]["local"]:              # Render local users OTP keys -            render(ocserv_otp_usr, 'ocserv/ocserv_otp_usr.tmpl', ocserv["authentication"]["local_users"]) +            render(ocserv_otp_usr, 'ocserv/ocserv_otp_usr.j2', ocserv["authentication"]["local_users"])          else:              # Render local users ocpasswd -            render(ocserv_passwd, 'ocserv/ocserv_passwd.tmpl', ocserv["authentication"]["local_users"]) +            render(ocserv_passwd, 'ocserv/ocserv_passwd.j2', ocserv["authentication"]["local_users"])      else:          if "local_users" in ocserv["authentication"]:              for user in ocserv["authentication"]["local_users"]["username"]:                  ocserv["authentication"]["local_users"]["username"][user]["hash"] = get_hash(ocserv["authentication"]["local_users"]["username"][user]["password"])              # Render local users -            render(ocserv_passwd, 'ocserv/ocserv_passwd.tmpl', ocserv["authentication"]["local_users"]) +            render(ocserv_passwd, 'ocserv/ocserv_passwd.j2', ocserv["authentication"]["local_users"])      if "ssl" in ocserv:          cert_file_path = os.path.join(cfg_dir, 'cert.pem') @@ -227,7 +227,7 @@ def generate(ocserv):                  f.write(wrap_certificate(pki_ca_cert['certificate']))      # Render config -    render(ocserv_conf, 'ocserv/ocserv_config.tmpl', ocserv) +    render(ocserv_conf, 'ocserv/ocserv_config.j2', ocserv)  def apply(ocserv): diff --git a/src/conf_mode/vpn_pptp.py b/src/conf_mode/vpn_pptp.py index 30abe4782..7550c411e 100755 --- a/src/conf_mode/vpn_pptp.py +++ b/src/conf_mode/vpn_pptp.py @@ -264,10 +264,10 @@ def generate(pptp):      if not pptp:          return None -    render(pptp_conf, 'accel-ppp/pptp.config.tmpl', pptp) +    render(pptp_conf, 'accel-ppp/pptp.config.j2', pptp)      if pptp['local_users']: -        render(pptp_chap_secrets, 'accel-ppp/chap-secrets.tmpl', pptp) +        render(pptp_chap_secrets, 'accel-ppp/chap-secrets.j2', pptp)          os.chmod(pptp_chap_secrets, S_IRUSR | S_IWUSR | S_IRGRP)      else:          if os.path.exists(pptp_chap_secrets): diff --git a/src/conf_mode/vpn_sstp.py b/src/conf_mode/vpn_sstp.py index 68980e5ab..db53463cf 100755 --- a/src/conf_mode/vpn_sstp.py +++ b/src/conf_mode/vpn_sstp.py @@ -114,7 +114,7 @@ def generate(sstp):          return None      # accel-cmd reload doesn't work so any change results in a restart of the daemon -    render(sstp_conf, 'accel-ppp/sstp.config.tmpl', sstp) +    render(sstp_conf, 'accel-ppp/sstp.config.j2', sstp)      cert_name = sstp['ssl']['certificate']      pki_cert = sstp['pki']['certificate'][cert_name] @@ -127,7 +127,7 @@ def generate(sstp):      write_file(ca_cert_file_path, wrap_certificate(pki_ca['certificate']))      if dict_search('authentication.mode', sstp) == 'local': -        render(sstp_chap_secrets, 'accel-ppp/chap-secrets.config_dict.tmpl', +        render(sstp_chap_secrets, 'accel-ppp/chap-secrets.config_dict.j2',                 sstp, permission=0o640)      else:          if os.path.exists(sstp_chap_secrets): diff --git a/src/conf_mode/vrf.py b/src/conf_mode/vrf.py index f79c8a21e..972d0289b 100755 --- a/src/conf_mode/vrf.py +++ b/src/conf_mode/vrf.py @@ -83,7 +83,8 @@ def get_config(config=None):          conf = Config()      base = ['vrf'] -    vrf = conf.get_config_dict(base, get_first_key=True) +    vrf = conf.get_config_dict(base, key_mangling=('-', '_'), +                               no_tag_node_value_mangle=True, get_first_key=True)      # determine which VRF has been removed      for name in node_changed(conf, base + ['name']): @@ -133,10 +134,10 @@ def verify(vrf):  def generate(vrf): -    render(config_file, 'vrf/vrf.conf.tmpl', vrf) +    render(config_file, 'vrf/vrf.conf.j2', vrf)      # Render nftables zones config -    render(nft_vrf_config, 'firewall/nftables-vrf-zones.tmpl', vrf) +    render(nft_vrf_config, 'firewall/nftables-vrf-zones.j2', vrf)      return None @@ -152,7 +153,7 @@ def apply(vrf):      # set the default VRF global behaviour      bind_all = '0' -    if 'bind-to-all' in vrf: +    if 'bind_to_all' in vrf:          bind_all = '1'      sysctl_write('net.ipv4.tcp_l3mdev_accept', bind_all)      sysctl_write('net.ipv4.udp_l3mdev_accept', bind_all) @@ -222,6 +223,15 @@ def apply(vrf):              # add VRF description if available              vrf_if.set_alias(config.get('description', '')) +            # Enable/Disable IPv4 forwarding +            tmp = dict_search('ip.disable_forwarding', config) +            value = '0' if (tmp != None) else '1' +            vrf_if.set_ipv4_forwarding(value) +            # Enable/Disable IPv6 forwarding +            tmp = dict_search('ipv6.disable_forwarding', config) +            value = '0' if (tmp != None) else '1' +            vrf_if.set_ipv6_forwarding(value) +              # Enable/Disable of an interface must always be done at the end of the              # derived class to make use of the ref-counting set_admin_state()              # function. We will only enable the interface if 'up' was called as diff --git a/src/conf_mode/zone_policy.py b/src/conf_mode/zone_policy.py index dc0617353..070a4deea 100755 --- a/src/conf_mode/zone_policy.py +++ b/src/conf_mode/zone_policy.py @@ -192,7 +192,7 @@ def generate(zone_policy):              if 'local_zone' in zone_conf:                  zone_conf['from_local'] = get_local_from(data, zone) -    render(nftables_conf, 'zone_policy/nftables.tmpl', data) +    render(nftables_conf, 'zone_policy/nftables.j2', data)      return None  def apply(zone_policy): diff --git a/src/etc/dhcp/dhclient-enter-hooks.d/03-vyos-ipwrapper b/src/etc/dhcp/dhclient-enter-hooks.d/03-vyos-ipwrapper index 74a7e83bf..5d879471d 100644 --- a/src/etc/dhcp/dhclient-enter-hooks.d/03-vyos-ipwrapper +++ b/src/etc/dhcp/dhclient-enter-hooks.d/03-vyos-ipwrapper @@ -26,7 +26,7 @@ function iptovtysh () {      local VTYSH_GATEWAY=""      local VTYSH_DEV=""      local VTYSH_TAG="210" -    local VTYSH_DISTANCE="" +    local VTYSH_DISTANCE=$IF_METRIC      # convert default route to 0.0.0.0/0      if [ "$4" == "default" ] ; then          VTYSH_NETADDR="0.0.0.0/0" diff --git a/src/migration-scripts/quagga/9-to-10 b/src/migration-scripts/quagga/9-to-10 new file mode 100755 index 000000000..249738822 --- /dev/null +++ b/src/migration-scripts/quagga/9-to-10 @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2022 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/>. + +# re-organize route-map as-path + +from sys import argv +from sys import exit + +from vyos.configtree import ConfigTree + +if (len(argv) < 2): +    print("Must specify file name!") +    exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: +    config_file = f.read() + +base = ['policy', 'route-map'] + +config = ConfigTree(config_file) +if not config.exists(base): +    # Nothing to do +    exit(0) + +for route_map in config.list_nodes(base): +    # Bail out Early +    if not config.exists(base + [route_map, 'rule']): +        continue + +    for rule in config.list_nodes(base + [route_map, 'rule']): +        rule_base = base + [route_map, 'rule', rule] +        if config.exists(rule_base + ['set', 'as-path-exclude']): +            tmp = config.return_value(rule_base + ['set', 'as-path-exclude']) +            config.delete(rule_base + ['set', 'as-path-exclude']) +            config.set(rule_base + ['set', 'as-path', 'exclude'], value=tmp) + +        if config.exists(rule_base + ['set', 'as-path-prepend']): +            tmp = config.return_value(rule_base + ['set', 'as-path-prepend']) +            config.delete(rule_base + ['set', 'as-path-prepend']) +            config.set(rule_base + ['set', 'as-path', 'prepend'], value=tmp) + +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) diff --git a/src/op_mode/conntrack_sync.py b/src/op_mode/conntrack_sync.py index 89f6df4b9..e45c38f07 100755 --- a/src/op_mode/conntrack_sync.py +++ b/src/op_mode/conntrack_sync.py @@ -77,7 +77,7 @@ def xml_to_stdout(xml):          parsed = xmltodict.parse(line)          out.append(parsed) -    print(render_to_string('conntrackd/conntrackd.op-mode.tmpl', {'data' : out})) +    print(render_to_string('conntrackd/conntrackd.op-mode.j2', {'data' : out}))  if __name__ == '__main__':      args = parser.parse_args() diff --git a/src/op_mode/containers_op.py b/src/op_mode/containers_op.py deleted file mode 100755 index c55a48b3c..000000000 --- a/src/op_mode/containers_op.py +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2021-2022 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 -import argparse - -from getpass import getuser -from vyos.configquery import ConfigTreeQuery -from vyos.base import Warning -from vyos.util import cmd -from subprocess import STDOUT - -parser = argparse.ArgumentParser() -parser.add_argument("-a", "--all", action="store_true", help="Show all containers") -parser.add_argument("-i", "--image", action="store_true", help="Show container images") -parser.add_argument("-n", "--networks", action="store_true", help="Show container images") -parser.add_argument("-p", "--pull", action="store", help="Pull image for container") -parser.add_argument("-d", "--remove", action="store", help="Delete container image") -parser.add_argument("-u", "--update", action="store", help="Update given container image") - -config = ConfigTreeQuery() -base = ['container'] - -if getuser() != 'root': -    raise OSError('This functions needs to be run as root to return correct results!') - -if __name__ == '__main__': -    args = parser.parse_args() - -    if args.all: -        print(cmd('podman ps --all')) -    elif args.image: -        print(cmd('podman image ls')) -    elif args.networks: -        print(cmd('podman network ls')) - -    elif args.pull: -        image = args.pull -        registry_config = '/etc/containers/registries.conf' -        if not os.path.exists(registry_config): -            Warning('No container registry configured. Please use full URL when '\ -                    'adding an image. E.g. prefix with docker.io/image-name.') -        try: -            print(os.system(f'podman image pull {image}')) -        except Exception  as e: -            print(f'Unable to download image "{image}". {e}') - -    elif args.remove: -        image = args.remove -        try: -            print(os.system(f'podman image rm {image}')) -        except FileNotFoundError as e: -            print(f'Unable to delete image "{image}". {e}') - -    elif args.update: -        tmp = config.get_config_dict(base + ['name', args.update], -                                     key_mangling=('-', '_'), get_first_key=True) -        try: -            image = tmp['image'] -            print(cmd(f'podman image pull {image}')) -        except Exception  as e: -            print(f'Unable to download image "{image}". {e}') -    else: -        parser.print_help() -        exit(1) - -    exit(0) diff --git a/src/op_mode/ikev2_profile_generator.py b/src/op_mode/ikev2_profile_generator.py index 990b06c12..21561d16f 100755 --- a/src/op_mode/ikev2_profile_generator.py +++ b/src/op_mode/ikev2_profile_generator.py @@ -222,9 +222,9 @@ except KeyboardInterrupt:  print('\n\n==== <snip> ====')  if args.os == 'ios': -    print(render_to_string('ipsec/ios_profile.tmpl', data)) +    print(render_to_string('ipsec/ios_profile.j2', data))      print('==== </snip> ====\n')      print('Save the XML from above to a new file named "vyos.mobileconfig" and E-Mail it to your phone.')  elif args.os == 'windows': -    print(render_to_string('ipsec/windows_profile.tmpl', data)) +    print(render_to_string('ipsec/windows_profile.j2', data))      print('==== </snip> ====\n') diff --git a/src/op_mode/show_openvpn.py b/src/op_mode/show_openvpn.py index f7b99cc0d..9a5adcffb 100755 --- a/src/op_mode/show_openvpn.py +++ b/src/op_mode/show_openvpn.py @@ -26,10 +26,10 @@ outp_tmpl = """  {% if clients %}  OpenVPN status on {{ intf }} -Client CN       Remote Host           Local Host            TX bytes    RX bytes   Connected Since ----------       -----------           ----------            --------    --------   --------------- +Client CN       Remote Host            Tunnel IP        Local Host            TX bytes    RX bytes   Connected Since +---------       -----------            ---------        ----------            --------    --------   ---------------  {% for c in clients %} -{{ "%-15s"|format(c.name) }} {{ "%-21s"|format(c.remote) }} {{ "%-21s"|format(local) }} {{ "%-9s"|format(c.tx_bytes) }}   {{ "%-9s"|format(c.rx_bytes) }}  {{ c.online_since }} +{{ "%-15s"|format(c.name) }}  {{ "%-21s"|format(c.remote) }}  {{ "%-15s"|format(c.tunnel) }}  {{ "%-21s"|format(local) }} {{ "%-9s"|format(c.tx_bytes) }}   {{ "%-9s"|format(c.rx_bytes) }}  {{ c.online_since }}  {% endfor %}  {% endif %}  """ @@ -50,6 +50,19 @@ def bytes2HR(size):      output="{0:.1f} {1}".format(size, suff[suffIdx])      return output +def get_vpn_tunnel_address(peer, interface): +    lst = [] +    status_file = '/var/run/openvpn/{}.status'.format(interface) + +    with open(status_file, 'r') as f: +        lines = f.readlines() +        for line in lines: +            if peer in line: +                lst.append(line) +        tunnel_ip = lst[1].split(',')[0] + +        return tunnel_ip +  def get_status(mode, interface):      status_file = '/var/run/openvpn/{}.status'.format(interface)      # this is an empirical value - I assume we have no more then 999999 @@ -110,7 +123,7 @@ def get_status(mode, interface):                          'tx_bytes': bytes2HR(line.split(',')[3]),                          'online_since': line.split(',')[4]                      } - +                    client["tunnel"] = get_vpn_tunnel_address(client['remote'], interface)                      data['clients'].append(client)                      continue              else: @@ -173,5 +186,7 @@ if __name__ == '__main__':                  if len(remote_host) >= 1:                      client['remote'] = str(remote_host[0]) + ':' + remote_port +                client['tunnel'] = 'N/A' +          tmpl = jinja2.Template(outp_tmpl)          print(tmpl.render(data)) diff --git a/src/op_mode/show_uptime.py b/src/op_mode/show_uptime.py index 1b5e33fa9..b70c60cf8 100755 --- a/src/op_mode/show_uptime.py +++ b/src/op_mode/show_uptime.py @@ -1,6 +1,6 @@  #!/usr/bin/env python3  # -# Copyright (C) 2021 VyOS maintainers and contributors +# Copyright (C) 2021-2022 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 as @@ -26,14 +26,17 @@ def get_uptime_seconds():  def get_load_averages():      from re import search      from vyos.util import cmd +    from vyos.cpu import get_core_count      data = cmd("uptime")      matches = search(r"load average:\s*(?P<one>[0-9\.]+)\s*,\s*(?P<five>[0-9\.]+)\s*,\s*(?P<fifteen>[0-9\.]+)\s*", data) +    core_count = get_core_count() +      res = {} -    res[1]  = float(matches["one"]) -    res[5]  = float(matches["five"]) -    res[15] = float(matches["fifteen"]) +    res[1]  = float(matches["one"]) / core_count +    res[5]  = float(matches["five"]) / core_count +    res[15] = float(matches["fifteen"]) / core_count      return res @@ -53,9 +56,9 @@ def get_formatted_output():      out = "Uptime: {}\n\n".format(data["uptime"])      avgs = data["load_average"]      out += "Load averages:\n" -    out += "1  minute:   {:.02f}%\n".format(avgs[1]*100) -    out += "5  minutes:  {:.02f}%\n".format(avgs[5]*100) -    out += "15 minutes:  {:.02f}%\n".format(avgs[15]*100) +    out += "1  minute:   {:.01f}%\n".format(avgs[1]*100) +    out += "5  minutes:  {:.01f}%\n".format(avgs[5]*100) +    out += "15 minutes:  {:.01f}%\n".format(avgs[15]*100)      return out diff --git a/src/op_mode/traceroute.py b/src/op_mode/traceroute.py new file mode 100755 index 000000000..4299d6e5f --- /dev/null +++ b/src/op_mode/traceroute.py @@ -0,0 +1,207 @@ +#! /usr/bin/env python3 + +# Copyright (C) 2022 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 +import sys +import socket +import ipaddress + +options = { +    'backward-hops': { +        'traceroute': '{command} --back', +        'type': 'noarg', +        'help': 'Display number of backward hops when they different from the forwarded path' +    }, +    'bypass': { +        'traceroute': '{command} -r', +        'type': 'noarg', +        'help': 'Bypass the normal routing tables and send directly to a host on an attached network' +    }, +    'do-not-fragment': { +        'traceroute': '{command} -F', +        'type': 'noarg', +        'help': 'Do not fragment probe packets.' +    }, +    'first-ttl': { +        'traceroute': '{command} -f {value}', +        'type': '<ttl>', +        'help': 'Specifies with what TTL to start. Defaults to 1.' +    }, +    'icmp': { +        'traceroute': '{command} -I', +        'type': 'noarg', +        'help': 'Use ICMP ECHO for tracerouting' +    }, +    'interface': { +        'traceroute': '{command} -i {value}', +        'type': '<interface>', +        'help': 'Source interface' +    }, +    'lookup-as': { +        'traceroute': '{command} -A', +        'type': 'noarg', +        'help': 'Perform AS path lookups' +    }, +    'mark': { +        'traceroute': '{command} --fwmark={value}', +        'type': '<fwmark>', +        'help': 'Set the firewall mark for outgoing packets' +    }, +    'no-resolve': { +        'traceroute': '{command} -n', +        'type': 'noarg', +        'help': 'Do not resolve hostnames' +    }, +    'port': { +        'traceroute': '{command} -p {value}', +        'type': '<port>', +        'help': 'Destination port' +    }, +    'source-address': { +        'traceroute': '{command} -s {value}', +        'type': '<x.x.x.x> <h:h:h:h:h:h:h:h>', +        'help': 'Specify source IP v4/v6 address' +    }, +    'tcp': { +        'traceroute': '{command} -T', +        'type': 'noarg', +        'help': 'Use TCP SYN for tracerouting (default port is 80)' +    }, +    'tos': { +        'traceroute': '{commad} -t {value}', +        'type': '<tos>', +        'help': 'Mark packets with specified TOS' +    }, +    'ttl': { +        'traceroute': '{command} -m {value}', +        'type': '<ttl>', +        'help': 'Maximum number of hops' +    }, +    'udp': { +        'traceroute': '{command} -U', +        'type': 'noarg', +        'help': 'Use UDP to particular port for tracerouting (default port is 53)' +    }, +    'vrf': { +        'traceroute': 'sudo ip vrf exec {value} {command}', +        'type': '<vrf>', +        'help': 'Use specified VRF table', +        'dflt': 'default'} +} + +traceroute = { +    4: '/bin/traceroute -4', +    6: '/bin/traceroute -6', +} + + +class List (list): +    def first (self): +        return self.pop(0) if self else '' + +    def last(self): +        return self.pop() if self else '' + +    def prepend(self,value): +        self.insert(0,value) + + +def expension_failure(option, completions): +    reason = 'Ambiguous' if completions else 'Invalid' +    sys.stderr.write('\n\n  {} command: {} [{}]\n\n'.format(reason,' '.join(sys.argv), option)) +    if completions: +        sys.stderr.write('  Possible completions:\n   ') +        sys.stderr.write('\n   '.join(completions)) +        sys.stderr.write('\n') +    sys.stdout.write('<nocomps>') +    sys.exit(1) + + +def complete(prefix): +    return [o for o in options if o.startswith(prefix)] + + +def convert(command, args): +    while args: +        shortname = args.first() +        longnames = complete(shortname) +        if len(longnames) != 1: +            expension_failure(shortname, longnames) +        longname = longnames[0] +        if options[longname]['type'] == 'noarg': +            command = options[longname]['traceroute'].format( +                command=command, value='') +        elif not args: +            sys.exit(f'traceroute: missing argument for {longname} option') +        else: +            command = options[longname]['traceroute'].format( +                command=command, value=args.first()) +    return command + + +if __name__ == '__main__': +    args = List(sys.argv[1:]) +    host = args.first() + +    if not host: +        sys.exit("traceroute: Missing host") + +    if host == '--get-options': +        args.first()  # pop traceroute +        args.first()  # pop IP +        while args: +            option = args.first() + +            matched = complete(option) +            if not args: +                sys.stdout.write(' '.join(matched)) +                sys.exit(0) + +            if len(matched) > 1 : +                sys.stdout.write(' '.join(matched)) +                sys.exit(0) + +            if options[matched[0]]['type'] == 'noarg': +                continue + +            value = args.first() +            if not args: +                matched = complete(option) +                sys.stdout.write(options[matched[0]]['type']) +                sys.exit(0) + +    for name,option in options.items(): +        if 'dflt' in option and name not in args: +            args.append(name) +            args.append(option['dflt']) + +    try: +        ip = socket.gethostbyname(host) +    except UnicodeError: +        sys.exit(f'tracroute: Unknown host: {host}') +    except socket.gaierror: +        ip = host + +    try: +        version = ipaddress.ip_address(ip).version +    except ValueError: +        sys.exit(f'traceroute: Unknown host: {host}') + +    command = convert(traceroute[version],args) + +    # print(f'{command} {host}') +    os.system(f'{command} {host}') + diff --git a/src/validators/as-number-list b/src/validators/as-number-list new file mode 100755 index 000000000..432d44180 --- /dev/null +++ b/src/validators/as-number-list @@ -0,0 +1,29 @@ +#!/bin/sh +# +# Copyright (C) 2022 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/>. + +if [ $# -lt 1 ]; then +      echo "Illegal number of parameters" +      exit 1 +fi + +for var in "$@"; do +    ${vyos_validators_dir}/numeric --range 1-4294967294 $var +    if [ $? -ne 0 ]; then +        exit 1 +    fi +done + +exit 0 | 
